refactor(core): refactor and fix event system following multiwebview support (#8621)

* clippy

* refactor(core): refactor and fix event system following multiwebview support

* update documentation

* update js docs

* lint

* clippy

* update multiwindow example [skip ci]

* enhance event tests

* fix example

* Update .changes/tauri-event-after-multiwebview.md

Co-authored-by: Lucas Nogueira <118899497+lucasfernog-crabnebula@users.noreply.github.com>

* fix tests

* add diagram

* Add `App/AppHandle` even target

* Discard changes to examples/api/src-tauri/tauri-plugin-sample/permissions/schemas/schema.json

* revert accidental changes

* regenerate schemas

* fix doctests

* add helper methods

* update docs

* update api

* update docs [skip ci]

* update docs [skip ci]

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
Co-authored-by: Lucas Nogueira <118899497+lucasfernog-crabnebula@users.noreply.github.com>
This commit is contained in:
Amr Bashir 2024-02-01 13:06:27 +02:00 committed by GitHub
parent 7fcc0bcd34
commit a093682d2d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 2472 additions and 5836 deletions

5
.changes/api-emit-to.md Normal file
View File

@ -0,0 +1,5 @@
---
'@tauri-apps/api': 'patch:feat'
---
Added `emitTo` api to `event` module which is equivalent to the rust `emit_to` method. Also added `emitTo` method on `Window`, `Webivew` and `WebviewWindow` classes.

View File

@ -1,5 +1,5 @@
---
"@tauri-apps/api": patch:breaking
'@tauri-apps/api': patch:breaking
---
Removed event callback's `windowLabel` field and added a `windowSource` object instead.
Removed event callback's `windowLabel`.

View File

@ -0,0 +1,5 @@
---
'tauri': 'patch:bug'
---
Fix can not prevent closing a window from another webview.

View File

@ -0,0 +1,12 @@
---
'tauri': 'patch:breaking'
---
Refactored the event system to better accommodate the new window types:
- Added `EventTarget` enum.
- Added `App/AppHandle::listen`, `App/AppHandle::once` and `App/AppHandle::unlisten` to listen to events targeting `App/AppHandle`
- `App/AppHandle/Window/Webview/WebviewWindow::emit` will now emit to all event listeners.
- `App/AppHandle/Window/Webview/WebviewWindow::emit_to` will emit to event targets that match the given label, see `EventTarget` enum.
- `App/AppHandle/Window/Webview/WebviewWindow::emit_filter` will emit to event targets based on a filter callback which now takes `&EventTarget` instead of `&Window`.
- Renamed `Manager::listen_global` and `Manager::once_global` to `listen_any` and `once_any` respectively to be consistent with `EventTarget::Any`, it will now also listen to any event to any target (aka event sniffer).

View File

@ -0,0 +1,5 @@
---
'tauri-utils': 'patch:breaking'
---
Changed `error` field in `ConfigError::FormatToml` to be boxed `Box<toml::de::Error>` to reduce the enum `ConfigError` size in memory.

View File

@ -2902,7 +2902,7 @@ fn handle_event_loop<T: UserEvent>(
if window.is_window_transparent {
if let Some(surface) = &mut window.surface {
if let Some(window) = &window.inner {
clear_window_surface(&window, surface)
clear_window_surface(window, surface)
}
}
}

View File

@ -108,7 +108,7 @@ pub enum ConfigError {
path: PathBuf,
/// The parsing [`toml::Error`].
error: ::toml::de::Error,
error: Box<::toml::de::Error>,
},
/// Unknown config file name encountered.
@ -381,7 +381,7 @@ fn do_parse_json5<D: DeserializeOwned>(raw: &str, path: &Path) -> Result<D, Conf
fn do_parse_toml<D: DeserializeOwned>(raw: &str, path: &Path) -> Result<D, ConfigError> {
::toml::from_str(raw).map_err(|error| ConfigError::FormatToml {
path: path.into(),
error,
error: Box::new(error),
})
}

View File

@ -32,7 +32,12 @@ const PLUGINS: &[(&str, &[(&str, bool)])] = &[
),
(
"event",
&[("listen", true), ("unlisten", true), ("emit", true)],
&[
("listen", true),
("unlisten", true),
("emit", true),
("emit_to", true),
],
),
(
"window",

View File

@ -1,339 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PermissionFile",
"description": "Permission file that can define a default permission, a set of permissions or a list of inlined permissions.",
"type": "object",
"properties": {
"default": {
"description": "The default permission set for the plugin",
"anyOf": [
{
"$ref": "#/definitions/DefaultPermission"
},
{
"type": "null"
}
]
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
},
"set": {
"description": "A list of permissions sets defined",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/PermissionSet"
}
},
"test": {
"description": "Test something!!",
"anyOf": [
{
"$ref": "#/definitions/PermissionSet"
},
{
"type": "null"
}
]
}
},
"definitions": {
"Commands": {
"description": "Allowed and denied commands inside a permission.\n\nIf two commands clash inside of `allow` and `deny`, it should be denied by default.",
"type": "object",
"properties": {
"allow": {
"description": "Allowed command.",
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"deny": {
"description": "Denied command, which takes priority.",
"default": [],
"type": "array",
"items": {
"type": "string"
}
}
}
},
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"Number": {
"description": "A valid ACL number.",
"anyOf": [
{
"description": "Represents an [`i64`].",
"type": "integer",
"format": "int64"
},
{
"description": "Represents a [`f64`].",
"type": "number",
"format": "double"
}
]
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {
"allow": null,
"deny": null
},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"PermissionKind": {
"type": "string",
"oneOf": [
{
"description": "allow-app-hide -> Enables the app_hide command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-app-hide"
]
},
{
"description": "deny-app-hide -> Denies the app_hide command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-app-hide"
]
},
{
"description": "allow-app-show -> Enables the app_show command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-app-show"
]
},
{
"description": "deny-app-show -> Denies the app_show command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-app-show"
]
},
{
"description": "allow-name -> Enables the name command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-name"
]
},
{
"description": "deny-name -> Denies the name command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-name"
]
},
{
"description": "allow-tauri-version -> Enables the tauri_version command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-tauri-version"
]
},
{
"description": "deny-tauri-version -> Denies the tauri_version command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-tauri-version"
]
},
{
"description": "allow-version -> Enables the version command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-version"
]
},
{
"description": "deny-version -> Denies the version command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-version"
]
},
{
"description": "default -> Default permissions for the plugin.",
"type": "string",
"enum": [
"default"
]
}
]
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Scopes": {
"description": "A restriction of the command/endpoint functionality.\n\nIt can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.\n\nThe scope is passed to the command and handled/enforced by the command itself.",
"type": "object",
"properties": {
"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"
}
}
}
},
"Value": {
"description": "All supported ACL values.",
"anyOf": [
{
"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"
}
}
]
}
}
}

View File

@ -15,14 +15,6 @@
}
]
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
},
"set": {
"description": "A list of permissions sets defined",
"default": [],
@ -30,9 +22,125 @@
"items": {
"$ref": "#/definitions/PermissionSet"
}
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
}
},
"definitions": {
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
}
}
},
"Commands": {
"description": "Allowed and denied commands inside a permission.\n\nIf two commands clash inside of `allow` and `deny`, it should be denied by default.",
"type": "object",
@ -55,38 +163,71 @@
}
}
},
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"Scopes": {
"description": "A restriction of the command/endpoint functionality.\n\nIt can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.\n\nThe scope is passed to the command and handled/enforced by the command itself.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"allow": {
"description": "Data that defines what is allowed by the scope.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"array",
"null"
],
"format": "uint64",
"minimum": 1.0
"items": {
"$ref": "#/definitions/Value"
}
},
"deny": {
"description": "Data that defines what is denied by the scope.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/Value"
}
}
}
},
"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": [
@ -102,56 +243,6 @@
}
]
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"PermissionKind": {
"type": "string",
"oneOf": [
@ -233,97 +324,6 @@
]
}
]
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Scopes": {
"description": "A restriction of the command/endpoint functionality.\n\nIt can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.\n\nThe scope is passed to the command and handled/enforced by the command itself.",
"type": "object",
"properties": {
"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"
}
}
}
},
"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"
}
}
]
}
}
}

View File

@ -1,311 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PermissionFile",
"description": "Permission file that can define a default permission, a set of permissions or a list of inlined permissions.",
"type": "object",
"properties": {
"default": {
"description": "The default permission set for the plugin",
"anyOf": [
{
"$ref": "#/definitions/DefaultPermission"
},
{
"type": "null"
}
]
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
},
"set": {
"description": "A list of permissions sets defined",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/PermissionSet"
}
},
"test": {
"description": "Test something!!",
"anyOf": [
{
"$ref": "#/definitions/PermissionSet"
},
{
"type": "null"
}
]
}
},
"definitions": {
"Commands": {
"description": "Allowed and denied commands inside a permission.\n\nIf two commands clash inside of `allow` and `deny`, it should be denied by default.",
"type": "object",
"properties": {
"allow": {
"description": "Allowed command.",
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"deny": {
"description": "Denied command, which takes priority.",
"default": [],
"type": "array",
"items": {
"type": "string"
}
}
}
},
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"Number": {
"description": "A valid ACL number.",
"anyOf": [
{
"description": "Represents an [`i64`].",
"type": "integer",
"format": "int64"
},
{
"description": "Represents a [`f64`].",
"type": "number",
"format": "double"
}
]
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {
"allow": null,
"deny": null
},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"PermissionKind": {
"type": "string",
"oneOf": [
{
"description": "allow-emit -> Enables the emit command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-emit"
]
},
{
"description": "deny-emit -> Denies the emit command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-emit"
]
},
{
"description": "allow-listen -> Enables the listen command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-listen"
]
},
{
"description": "deny-listen -> Denies the listen command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-listen"
]
},
{
"description": "allow-unlisten -> Enables the unlisten command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-unlisten"
]
},
{
"description": "deny-unlisten -> Denies the unlisten command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-unlisten"
]
},
{
"description": "default -> Default permissions for the plugin.",
"type": "string",
"enum": [
"default"
]
}
]
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Scopes": {
"description": "A restriction of the command/endpoint functionality.\n\nIt can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.\n\nThe scope is passed to the command and handled/enforced by the command itself.",
"type": "object",
"properties": {
"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"
}
}
}
},
"Value": {
"description": "All supported ACL values.",
"anyOf": [
{
"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"
}
}
]
}
}
}

View File

@ -0,0 +1,16 @@
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
# SPDX-License-Identifier: Apache-2.0
# SPDX-License-Identifier: MIT
# Automatically generated - DO NOT EDIT!
"$schema" = "../../../schemas/schema.json"
[[permission]]
identifier = "allow-emit-to"
description = "Enables the emit_to command without any pre-configured scope."
commands.allow = ["emit_to"]
[[permission]]
identifier = "deny-emit-to"
description = "Denies the emit_to command without any pre-configured scope."
commands.deny = ["emit_to"]

View File

@ -5,4 +5,4 @@
[default]
description = "Default permissions for the plugin."
permissions = ["allow-listen", "allow-unlisten", "allow-emit"]
permissions = ["allow-listen", "allow-unlisten", "allow-emit", "allow-emit-to"]

View File

@ -15,14 +15,6 @@
}
]
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
},
"set": {
"description": "A list of permissions sets defined",
"default": [],
@ -30,9 +22,125 @@
"items": {
"$ref": "#/definitions/PermissionSet"
}
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
}
},
"definitions": {
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
}
}
},
"Commands": {
"description": "Allowed and denied commands inside a permission.\n\nIf two commands clash inside of `allow` and `deny`, it should be denied by default.",
"type": "object",
@ -55,183 +163,6 @@
}
}
},
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"Number": {
"description": "A valid ACL number.",
"anyOf": [
{
"description": "Represents an [`i64`].",
"type": "integer",
"format": "int64"
},
{
"description": "Represents a [`f64`].",
"type": "number",
"format": "double"
}
]
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"PermissionKind": {
"type": "string",
"oneOf": [
{
"description": "allow-emit -> Enables the emit command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-emit"
]
},
{
"description": "deny-emit -> Denies the emit command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-emit"
]
},
{
"description": "allow-listen -> Enables the listen command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-listen"
]
},
{
"description": "deny-listen -> Denies the listen command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-listen"
]
},
{
"description": "allow-unlisten -> Enables the unlisten command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-unlisten"
]
},
{
"description": "deny-unlisten -> Denies the unlisten command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-unlisten"
]
},
{
"description": "default -> Default permissions for the plugin.",
"type": "string",
"enum": [
"default"
]
}
]
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Scopes": {
"description": "A restriction of the command/endpoint functionality.\n\nIt can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.\n\nThe scope is passed to the command and handled/enforced by the command itself.",
"type": "object",
@ -296,6 +227,89 @@
}
}
]
},
"Number": {
"description": "A valid ACL number.",
"anyOf": [
{
"description": "Represents an [`i64`].",
"type": "integer",
"format": "int64"
},
{
"description": "Represents a [`f64`].",
"type": "number",
"format": "double"
}
]
},
"PermissionKind": {
"type": "string",
"oneOf": [
{
"description": "allow-emit -> Enables the emit command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-emit"
]
},
{
"description": "deny-emit -> Denies the emit command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-emit"
]
},
{
"description": "allow-emit-to -> Enables the emit_to command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-emit-to"
]
},
{
"description": "deny-emit-to -> Denies the emit_to command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-emit-to"
]
},
{
"description": "allow-listen -> Enables the listen command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-listen"
]
},
{
"description": "deny-listen -> Denies the listen command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-listen"
]
},
{
"description": "allow-unlisten -> Enables the unlisten command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-unlisten"
]
},
{
"description": "deny-unlisten -> Denies the unlisten command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-unlisten"
]
},
{
"description": "default -> Default permissions for the plugin.",
"type": "string",
"enum": [
"default"
]
}
]
}
}
}

View File

@ -1,577 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PermissionFile",
"description": "Permission file that can define a default permission, a set of permissions or a list of inlined permissions.",
"type": "object",
"properties": {
"default": {
"description": "The default permission set for the plugin",
"anyOf": [
{
"$ref": "#/definitions/DefaultPermission"
},
{
"type": "null"
}
]
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
},
"set": {
"description": "A list of permissions sets defined",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/PermissionSet"
}
},
"test": {
"description": "Test something!!",
"anyOf": [
{
"$ref": "#/definitions/PermissionSet"
},
{
"type": "null"
}
]
}
},
"definitions": {
"Commands": {
"description": "Allowed and denied commands inside a permission.\n\nIf two commands clash inside of `allow` and `deny`, it should be denied by default.",
"type": "object",
"properties": {
"allow": {
"description": "Allowed command.",
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"deny": {
"description": "Denied command, which takes priority.",
"default": [],
"type": "array",
"items": {
"type": "string"
}
}
}
},
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"Number": {
"description": "A valid ACL number.",
"anyOf": [
{
"description": "Represents an [`i64`].",
"type": "integer",
"format": "int64"
},
{
"description": "Represents a [`f64`].",
"type": "number",
"format": "double"
}
]
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {
"allow": null,
"deny": null
},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"PermissionKind": {
"type": "string",
"oneOf": [
{
"description": "allow-append -> Enables the append command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-append"
]
},
{
"description": "deny-append -> Denies the append command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-append"
]
},
{
"description": "allow-create-default -> Enables the create_default command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-create-default"
]
},
{
"description": "deny-create-default -> Denies the create_default command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-create-default"
]
},
{
"description": "allow-get -> Enables the get command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-get"
]
},
{
"description": "deny-get -> Denies the get command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-get"
]
},
{
"description": "allow-insert -> Enables the insert command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-insert"
]
},
{
"description": "deny-insert -> Denies the insert command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-insert"
]
},
{
"description": "allow-is-checked -> Enables the is_checked command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-is-checked"
]
},
{
"description": "deny-is-checked -> Denies the is_checked command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-is-checked"
]
},
{
"description": "allow-is-enabled -> Enables the is_enabled command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-is-enabled"
]
},
{
"description": "deny-is-enabled -> Denies the is_enabled command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-is-enabled"
]
},
{
"description": "allow-items -> Enables the items command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-items"
]
},
{
"description": "deny-items -> Denies the items command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-items"
]
},
{
"description": "allow-new -> Enables the new command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-new"
]
},
{
"description": "deny-new -> Denies the new command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-new"
]
},
{
"description": "allow-popup -> Enables the popup command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-popup"
]
},
{
"description": "deny-popup -> Denies the popup command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-popup"
]
},
{
"description": "allow-prepend -> Enables the prepend command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-prepend"
]
},
{
"description": "deny-prepend -> Denies the prepend command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-prepend"
]
},
{
"description": "allow-remove -> Enables the remove command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-remove"
]
},
{
"description": "deny-remove -> Denies the remove command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-remove"
]
},
{
"description": "allow-remove-at -> Enables the remove_at command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-remove-at"
]
},
{
"description": "deny-remove-at -> Denies the remove_at command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-remove-at"
]
},
{
"description": "allow-set-accelerator -> Enables the set_accelerator command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-set-accelerator"
]
},
{
"description": "deny-set-accelerator -> Denies the set_accelerator command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-set-accelerator"
]
},
{
"description": "allow-set-as-app-menu -> Enables the set_as_app_menu command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-set-as-app-menu"
]
},
{
"description": "deny-set-as-app-menu -> Denies the set_as_app_menu command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-set-as-app-menu"
]
},
{
"description": "allow-set-as-help-menu-for-nsapp -> Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-set-as-help-menu-for-nsapp"
]
},
{
"description": "deny-set-as-help-menu-for-nsapp -> Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-set-as-help-menu-for-nsapp"
]
},
{
"description": "allow-set-as-window-menu -> Enables the set_as_window_menu command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-set-as-window-menu"
]
},
{
"description": "deny-set-as-window-menu -> Denies the set_as_window_menu command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-set-as-window-menu"
]
},
{
"description": "allow-set-as-windows-menu-for-nsapp -> Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-set-as-windows-menu-for-nsapp"
]
},
{
"description": "deny-set-as-windows-menu-for-nsapp -> Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-set-as-windows-menu-for-nsapp"
]
},
{
"description": "allow-set-checked -> Enables the set_checked command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-set-checked"
]
},
{
"description": "deny-set-checked -> Denies the set_checked command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-set-checked"
]
},
{
"description": "allow-set-enabled -> Enables the set_enabled command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-set-enabled"
]
},
{
"description": "deny-set-enabled -> Denies the set_enabled command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-set-enabled"
]
},
{
"description": "allow-set-icon -> Enables the set_icon command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-set-icon"
]
},
{
"description": "deny-set-icon -> Denies the set_icon command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-set-icon"
]
},
{
"description": "allow-set-text -> Enables the set_text command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-set-text"
]
},
{
"description": "deny-set-text -> Denies the set_text command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-set-text"
]
},
{
"description": "allow-text -> Enables the text command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-text"
]
},
{
"description": "deny-text -> Denies the text command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-text"
]
},
{
"description": "default -> Default permissions for the plugin.",
"type": "string",
"enum": [
"default"
]
}
]
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Scopes": {
"description": "A restriction of the command/endpoint functionality.\n\nIt can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.\n\nThe scope is passed to the command and handled/enforced by the command itself.",
"type": "object",
"properties": {
"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"
}
}
}
},
"Value": {
"description": "All supported ACL values.",
"anyOf": [
{
"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"
}
}
]
}
}
}

View File

@ -15,14 +15,6 @@
}
]
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
},
"set": {
"description": "A list of permissions sets defined",
"default": [],
@ -30,9 +22,125 @@
"items": {
"$ref": "#/definitions/PermissionSet"
}
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
}
},
"definitions": {
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
}
}
},
"Commands": {
"description": "Allowed and denied commands inside a permission.\n\nIf two commands clash inside of `allow` and `deny`, it should be denied by default.",
"type": "object",
@ -55,38 +163,71 @@
}
}
},
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"Scopes": {
"description": "A restriction of the command/endpoint functionality.\n\nIt can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.\n\nThe scope is passed to the command and handled/enforced by the command itself.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"allow": {
"description": "Data that defines what is allowed by the scope.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"array",
"null"
],
"format": "uint64",
"minimum": 1.0
"items": {
"$ref": "#/definitions/Value"
}
},
"deny": {
"description": "Data that defines what is denied by the scope.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/Value"
}
}
}
},
"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": [
@ -102,56 +243,6 @@
}
]
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"PermissionKind": {
"type": "string",
"oneOf": [
@ -471,97 +562,6 @@
]
}
]
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Scopes": {
"description": "A restriction of the command/endpoint functionality.\n\nIt can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.\n\nThe scope is passed to the command and handled/enforced by the command itself.",
"type": "object",
"properties": {
"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"
}
}
}
},
"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"
}
}
]
}
}
}

View File

@ -1,381 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PermissionFile",
"description": "Permission file that can define a default permission, a set of permissions or a list of inlined permissions.",
"type": "object",
"properties": {
"default": {
"description": "The default permission set for the plugin",
"anyOf": [
{
"$ref": "#/definitions/DefaultPermission"
},
{
"type": "null"
}
]
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
},
"set": {
"description": "A list of permissions sets defined",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/PermissionSet"
}
},
"test": {
"description": "Test something!!",
"anyOf": [
{
"$ref": "#/definitions/PermissionSet"
},
{
"type": "null"
}
]
}
},
"definitions": {
"Commands": {
"description": "Allowed and denied commands inside a permission.\n\nIf two commands clash inside of `allow` and `deny`, it should be denied by default.",
"type": "object",
"properties": {
"allow": {
"description": "Allowed command.",
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"deny": {
"description": "Denied command, which takes priority.",
"default": [],
"type": "array",
"items": {
"type": "string"
}
}
}
},
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"Number": {
"description": "A valid ACL number.",
"anyOf": [
{
"description": "Represents an [`i64`].",
"type": "integer",
"format": "int64"
},
{
"description": "Represents a [`f64`].",
"type": "number",
"format": "double"
}
]
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {
"allow": null,
"deny": null
},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"PermissionKind": {
"type": "string",
"oneOf": [
{
"description": "allow-basename -> Enables the basename command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-basename"
]
},
{
"description": "deny-basename -> Denies the basename command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-basename"
]
},
{
"description": "allow-dirname -> Enables the dirname command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-dirname"
]
},
{
"description": "deny-dirname -> Denies the dirname command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-dirname"
]
},
{
"description": "allow-extname -> Enables the extname command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-extname"
]
},
{
"description": "deny-extname -> Denies the extname command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-extname"
]
},
{
"description": "allow-is-absolute -> Enables the is_absolute command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-is-absolute"
]
},
{
"description": "deny-is-absolute -> Denies the is_absolute command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-is-absolute"
]
},
{
"description": "allow-join -> Enables the join command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-join"
]
},
{
"description": "deny-join -> Denies the join command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-join"
]
},
{
"description": "allow-normalize -> Enables the normalize command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-normalize"
]
},
{
"description": "deny-normalize -> Denies the normalize command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-normalize"
]
},
{
"description": "allow-resolve -> Enables the resolve command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-resolve"
]
},
{
"description": "deny-resolve -> Denies the resolve command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-resolve"
]
},
{
"description": "allow-resolve-directory -> Enables the resolve_directory command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-resolve-directory"
]
},
{
"description": "deny-resolve-directory -> Denies the resolve_directory command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-resolve-directory"
]
},
{
"description": "default -> Default permissions for the plugin.",
"type": "string",
"enum": [
"default"
]
}
]
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Scopes": {
"description": "A restriction of the command/endpoint functionality.\n\nIt can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.\n\nThe scope is passed to the command and handled/enforced by the command itself.",
"type": "object",
"properties": {
"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"
}
}
}
},
"Value": {
"description": "All supported ACL values.",
"anyOf": [
{
"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"
}
}
]
}
}
}

View File

@ -15,14 +15,6 @@
}
]
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
},
"set": {
"description": "A list of permissions sets defined",
"default": [],
@ -30,9 +22,125 @@
"items": {
"$ref": "#/definitions/PermissionSet"
}
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
}
},
"definitions": {
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
}
}
},
"Commands": {
"description": "Allowed and denied commands inside a permission.\n\nIf two commands clash inside of `allow` and `deny`, it should be denied by default.",
"type": "object",
@ -55,38 +163,71 @@
}
}
},
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"Scopes": {
"description": "A restriction of the command/endpoint functionality.\n\nIt can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.\n\nThe scope is passed to the command and handled/enforced by the command itself.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"allow": {
"description": "Data that defines what is allowed by the scope.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"array",
"null"
],
"format": "uint64",
"minimum": 1.0
"items": {
"$ref": "#/definitions/Value"
}
},
"deny": {
"description": "Data that defines what is denied by the scope.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/Value"
}
}
}
},
"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": [
@ -102,56 +243,6 @@
}
]
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"PermissionKind": {
"type": "string",
"oneOf": [
@ -275,97 +366,6 @@
]
}
]
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Scopes": {
"description": "A restriction of the command/endpoint functionality.\n\nIt can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.\n\nThe scope is passed to the command and handled/enforced by the command itself.",
"type": "object",
"properties": {
"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"
}
}
}
},
"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"
}
}
]
}
}
}

View File

@ -1,283 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PermissionFile",
"description": "Permission file that can define a default permission, a set of permissions or a list of inlined permissions.",
"type": "object",
"properties": {
"default": {
"description": "The default permission set for the plugin",
"anyOf": [
{
"$ref": "#/definitions/DefaultPermission"
},
{
"type": "null"
}
]
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
},
"set": {
"description": "A list of permissions sets defined",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/PermissionSet"
}
},
"test": {
"description": "Test something!!",
"anyOf": [
{
"$ref": "#/definitions/PermissionSet"
},
{
"type": "null"
}
]
}
},
"definitions": {
"Commands": {
"description": "Allowed and denied commands inside a permission.\n\nIf two commands clash inside of `allow` and `deny`, it should be denied by default.",
"type": "object",
"properties": {
"allow": {
"description": "Allowed command.",
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"deny": {
"description": "Denied command, which takes priority.",
"default": [],
"type": "array",
"items": {
"type": "string"
}
}
}
},
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"Number": {
"description": "A valid ACL number.",
"anyOf": [
{
"description": "Represents an [`i64`].",
"type": "integer",
"format": "int64"
},
{
"description": "Represents a [`f64`].",
"type": "number",
"format": "double"
}
]
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {
"allow": null,
"deny": null
},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"PermissionKind": {
"type": "string",
"oneOf": [
{
"description": "allow-close -> Enables the close command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-close"
]
},
{
"description": "deny-close -> Denies the close command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-close"
]
},
{
"description": "default -> Default permissions for the plugin.",
"type": "string",
"enum": [
"default"
]
}
]
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Scopes": {
"description": "A restriction of the command/endpoint functionality.\n\nIt can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.\n\nThe scope is passed to the command and handled/enforced by the command itself.",
"type": "object",
"properties": {
"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"
}
}
}
},
"Value": {
"description": "All supported ACL values.",
"anyOf": [
{
"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"
}
}
]
}
}
}

View File

@ -15,14 +15,6 @@
}
]
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
},
"set": {
"description": "A list of permissions sets defined",
"default": [],
@ -30,9 +22,125 @@
"items": {
"$ref": "#/definitions/PermissionSet"
}
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
}
},
"definitions": {
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
}
}
},
"Commands": {
"description": "Allowed and denied commands inside a permission.\n\nIf two commands clash inside of `allow` and `deny`, it should be denied by default.",
"type": "object",
@ -55,155 +163,6 @@
}
}
},
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"Number": {
"description": "A valid ACL number.",
"anyOf": [
{
"description": "Represents an [`i64`].",
"type": "integer",
"format": "int64"
},
{
"description": "Represents a [`f64`].",
"type": "number",
"format": "double"
}
]
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"PermissionKind": {
"type": "string",
"oneOf": [
{
"description": "allow-close -> Enables the close command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-close"
]
},
{
"description": "deny-close -> Denies the close command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-close"
]
},
{
"description": "default -> Default permissions for the plugin.",
"type": "string",
"enum": [
"default"
]
}
]
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Scopes": {
"description": "A restriction of the command/endpoint functionality.\n\nIt can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.\n\nThe scope is passed to the command and handled/enforced by the command itself.",
"type": "object",
@ -268,6 +227,47 @@
}
}
]
},
"Number": {
"description": "A valid ACL number.",
"anyOf": [
{
"description": "Represents an [`i64`].",
"type": "integer",
"format": "int64"
},
{
"description": "Represents a [`f64`].",
"type": "number",
"format": "double"
}
]
},
"PermissionKind": {
"type": "string",
"oneOf": [
{
"description": "allow-close -> Enables the close command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-close"
]
},
{
"description": "deny-close -> Denies the close command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-close"
]
},
{
"description": "default -> Default permissions for the plugin.",
"type": "string",
"enum": [
"default"
]
}
]
}
}
}

View File

@ -1,395 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PermissionFile",
"description": "Permission file that can define a default permission, a set of permissions or a list of inlined permissions.",
"type": "object",
"properties": {
"default": {
"description": "The default permission set for the plugin",
"anyOf": [
{
"$ref": "#/definitions/DefaultPermission"
},
{
"type": "null"
}
]
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
},
"set": {
"description": "A list of permissions sets defined",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/PermissionSet"
}
},
"test": {
"description": "Test something!!",
"anyOf": [
{
"$ref": "#/definitions/PermissionSet"
},
{
"type": "null"
}
]
}
},
"definitions": {
"Commands": {
"description": "Allowed and denied commands inside a permission.\n\nIf two commands clash inside of `allow` and `deny`, it should be denied by default.",
"type": "object",
"properties": {
"allow": {
"description": "Allowed command.",
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"deny": {
"description": "Denied command, which takes priority.",
"default": [],
"type": "array",
"items": {
"type": "string"
}
}
}
},
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"Number": {
"description": "A valid ACL number.",
"anyOf": [
{
"description": "Represents an [`i64`].",
"type": "integer",
"format": "int64"
},
{
"description": "Represents a [`f64`].",
"type": "number",
"format": "double"
}
]
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {
"allow": null,
"deny": null
},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"PermissionKind": {
"type": "string",
"oneOf": [
{
"description": "allow-new -> Enables the new command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-new"
]
},
{
"description": "deny-new -> Denies the new command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-new"
]
},
{
"description": "allow-set-icon -> Enables the set_icon command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-set-icon"
]
},
{
"description": "deny-set-icon -> Denies the set_icon command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-set-icon"
]
},
{
"description": "allow-set-icon-as-template -> Enables the set_icon_as_template command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-set-icon-as-template"
]
},
{
"description": "deny-set-icon-as-template -> Denies the set_icon_as_template command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-set-icon-as-template"
]
},
{
"description": "allow-set-menu -> Enables the set_menu command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-set-menu"
]
},
{
"description": "deny-set-menu -> Denies the set_menu command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-set-menu"
]
},
{
"description": "allow-set-show-menu-on-left-click -> Enables the set_show_menu_on_left_click command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-set-show-menu-on-left-click"
]
},
{
"description": "deny-set-show-menu-on-left-click -> Denies the set_show_menu_on_left_click command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-set-show-menu-on-left-click"
]
},
{
"description": "allow-set-temp-dir-path -> Enables the set_temp_dir_path command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-set-temp-dir-path"
]
},
{
"description": "deny-set-temp-dir-path -> Denies the set_temp_dir_path command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-set-temp-dir-path"
]
},
{
"description": "allow-set-title -> Enables the set_title command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-set-title"
]
},
{
"description": "deny-set-title -> Denies the set_title command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-set-title"
]
},
{
"description": "allow-set-tooltip -> Enables the set_tooltip command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-set-tooltip"
]
},
{
"description": "deny-set-tooltip -> Denies the set_tooltip command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-set-tooltip"
]
},
{
"description": "allow-set-visible -> Enables the set_visible command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-set-visible"
]
},
{
"description": "deny-set-visible -> Denies the set_visible command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-set-visible"
]
},
{
"description": "default -> Default permissions for the plugin.",
"type": "string",
"enum": [
"default"
]
}
]
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Scopes": {
"description": "A restriction of the command/endpoint functionality.\n\nIt can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.\n\nThe scope is passed to the command and handled/enforced by the command itself.",
"type": "object",
"properties": {
"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"
}
}
}
},
"Value": {
"description": "All supported ACL values.",
"anyOf": [
{
"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"
}
}
]
}
}
}

View File

@ -15,14 +15,6 @@
}
]
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
},
"set": {
"description": "A list of permissions sets defined",
"default": [],
@ -30,9 +22,125 @@
"items": {
"$ref": "#/definitions/PermissionSet"
}
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
}
},
"definitions": {
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
}
}
},
"Commands": {
"description": "Allowed and denied commands inside a permission.\n\nIf two commands clash inside of `allow` and `deny`, it should be denied by default.",
"type": "object",
@ -55,38 +163,71 @@
}
}
},
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"Scopes": {
"description": "A restriction of the command/endpoint functionality.\n\nIt can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.\n\nThe scope is passed to the command and handled/enforced by the command itself.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"allow": {
"description": "Data that defines what is allowed by the scope.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"array",
"null"
],
"format": "uint64",
"minimum": 1.0
"items": {
"$ref": "#/definitions/Value"
}
},
"deny": {
"description": "Data that defines what is denied by the scope.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/Value"
}
}
}
},
"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": [
@ -102,56 +243,6 @@
}
]
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"PermissionKind": {
"type": "string",
"oneOf": [
@ -289,97 +380,6 @@
]
}
]
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Scopes": {
"description": "A restriction of the command/endpoint functionality.\n\nIt can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.\n\nThe scope is passed to the command and handled/enforced by the command itself.",
"type": "object",
"properties": {
"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"
}
}
}
},
"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"
}
}
]
}
}
}

View File

@ -1,409 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PermissionFile",
"description": "Permission file that can define a default permission, a set of permissions or a list of inlined permissions.",
"type": "object",
"properties": {
"default": {
"description": "The default permission set for the plugin",
"anyOf": [
{
"$ref": "#/definitions/DefaultPermission"
},
{
"type": "null"
}
]
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
},
"set": {
"description": "A list of permissions sets defined",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/PermissionSet"
}
},
"test": {
"description": "Test something!!",
"anyOf": [
{
"$ref": "#/definitions/PermissionSet"
},
{
"type": "null"
}
]
}
},
"definitions": {
"Commands": {
"description": "Allowed and denied commands inside a permission.\n\nIf two commands clash inside of `allow` and `deny`, it should be denied by default.",
"type": "object",
"properties": {
"allow": {
"description": "Allowed command.",
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"deny": {
"description": "Denied command, which takes priority.",
"default": [],
"type": "array",
"items": {
"type": "string"
}
}
}
},
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"Number": {
"description": "A valid ACL number.",
"anyOf": [
{
"description": "Represents an [`i64`].",
"type": "integer",
"format": "int64"
},
{
"description": "Represents a [`f64`].",
"type": "number",
"format": "double"
}
]
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {
"allow": null,
"deny": null
},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"PermissionKind": {
"type": "string",
"oneOf": [
{
"description": "allow-create-webview -> Enables the create_webview command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-create-webview"
]
},
{
"description": "deny-create-webview -> Denies the create_webview command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-create-webview"
]
},
{
"description": "allow-create-webview-window -> Enables the create_webview_window command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-create-webview-window"
]
},
{
"description": "deny-create-webview-window -> Denies the create_webview_window command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-create-webview-window"
]
},
{
"description": "allow-internal-toggle-devtools -> Enables the internal_toggle_devtools command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-internal-toggle-devtools"
]
},
{
"description": "deny-internal-toggle-devtools -> Denies the internal_toggle_devtools command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-internal-toggle-devtools"
]
},
{
"description": "allow-print -> Enables the print command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-print"
]
},
{
"description": "deny-print -> Denies the print command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-print"
]
},
{
"description": "allow-set-webview-focus -> Enables the set_webview_focus command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-set-webview-focus"
]
},
{
"description": "deny-set-webview-focus -> Denies the set_webview_focus command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-set-webview-focus"
]
},
{
"description": "allow-set-webview-position -> Enables the set_webview_position command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-set-webview-position"
]
},
{
"description": "deny-set-webview-position -> Denies the set_webview_position command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-set-webview-position"
]
},
{
"description": "allow-set-webview-size -> Enables the set_webview_size command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-set-webview-size"
]
},
{
"description": "deny-set-webview-size -> Denies the set_webview_size command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-set-webview-size"
]
},
{
"description": "allow-webview-close -> Enables the webview_close command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-webview-close"
]
},
{
"description": "deny-webview-close -> Denies the webview_close command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-webview-close"
]
},
{
"description": "allow-webview-position -> Enables the webview_position command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-webview-position"
]
},
{
"description": "deny-webview-position -> Denies the webview_position command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-webview-position"
]
},
{
"description": "allow-webview-size -> Enables the webview_size command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-webview-size"
]
},
{
"description": "deny-webview-size -> Denies the webview_size command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-webview-size"
]
},
{
"description": "default -> Default permissions for the plugin.",
"type": "string",
"enum": [
"default"
]
}
]
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Scopes": {
"description": "A restriction of the command/endpoint functionality.\n\nIt can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.\n\nThe scope is passed to the command and handled/enforced by the command itself.",
"type": "object",
"properties": {
"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"
}
}
}
},
"Value": {
"description": "All supported ACL values.",
"anyOf": [
{
"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"
}
}
]
}
}
}

View File

@ -15,14 +15,6 @@
}
]
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
},
"set": {
"description": "A list of permissions sets defined",
"default": [],
@ -30,9 +22,125 @@
"items": {
"$ref": "#/definitions/PermissionSet"
}
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
}
},
"definitions": {
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
}
}
},
"Commands": {
"description": "Allowed and denied commands inside a permission.\n\nIf two commands clash inside of `allow` and `deny`, it should be denied by default.",
"type": "object",
@ -55,38 +163,71 @@
}
}
},
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"Scopes": {
"description": "A restriction of the command/endpoint functionality.\n\nIt can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.\n\nThe scope is passed to the command and handled/enforced by the command itself.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"allow": {
"description": "Data that defines what is allowed by the scope.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"array",
"null"
],
"format": "uint64",
"minimum": 1.0
"items": {
"$ref": "#/definitions/Value"
}
},
"deny": {
"description": "Data that defines what is denied by the scope.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/Value"
}
}
}
},
"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": [
@ -102,56 +243,6 @@
}
]
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"PermissionKind": {
"type": "string",
"oneOf": [
@ -303,97 +394,6 @@
]
}
]
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Scopes": {
"description": "A restriction of the command/endpoint functionality.\n\nIt can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.\n\nThe scope is passed to the command and handled/enforced by the command itself.",
"type": "object",
"properties": {
"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"
}
}
}
},
"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"
}
}
]
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -15,14 +15,6 @@
}
]
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
},
"set": {
"description": "A list of permissions sets defined",
"default": [],
@ -30,9 +22,125 @@
"items": {
"$ref": "#/definitions/PermissionSet"
}
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
}
},
"definitions": {
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
}
}
},
"Commands": {
"description": "Allowed and denied commands inside a permission.\n\nIf two commands clash inside of `allow` and `deny`, it should be denied by default.",
"type": "object",
@ -55,38 +163,71 @@
}
}
},
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"Scopes": {
"description": "A restriction of the command/endpoint functionality.\n\nIt can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.\n\nThe scope is passed to the command and handled/enforced by the command itself.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"allow": {
"description": "Data that defines what is allowed by the scope.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"array",
"null"
],
"format": "uint64",
"minimum": 1.0
"items": {
"$ref": "#/definitions/Value"
}
},
"deny": {
"description": "Data that defines what is denied by the scope.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/Value"
}
}
}
},
"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": [
@ -102,56 +243,6 @@
}
]
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
},
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
}
}
},
"PermissionKind": {
"type": "string",
"oneOf": [
@ -225,6 +316,20 @@
"deny-current-monitor"
]
},
{
"description": "allow-destroy -> Enables the destroy command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-destroy"
]
},
{
"description": "deny-destroy -> Denies the destroy command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-destroy"
]
},
{
"description": "allow-hide -> Enables the hide command without any pre-configured scope.",
"type": "string",
@ -1017,97 +1122,6 @@
]
}
]
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Scopes": {
"description": "A restriction of the command/endpoint functionality.\n\nIt can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.\n\nThe scope is passed to the command and handled/enforced by the command itself.",
"type": "object",
"properties": {
"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"
}
}
}
},
"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"
}
}
]
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -49,7 +49,7 @@ use std::{
sync::{mpsc::Sender, Arc},
};
use crate::runtime::RuntimeHandle;
use crate::{event::EventId, runtime::RuntimeHandle, Event, EventTarget};
#[cfg(target_os = "macos")]
use crate::ActivationPolicy;
@ -767,6 +767,65 @@ macro_rules! shared_app_impl {
self.resources_table().clear();
}
}
/// Event system APIs.
impl<R: Runtime> $app {
/// Listen to an event on this app.
///
/// # Examples
///
/// ```
/// use tauri::Manager;
///
/// tauri::Builder::default()
/// .setup(|app| {
/// app.listen("component-loaded", move |event| {
/// println!("window just loaded a component");
/// });
///
/// Ok(())
/// });
/// ```
pub fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: Fn(Event) + Send + 'static,
{
self.manager.listen(event.into(), EventTarget::App, handler)
}
/// Unlisten to an event on this app.
///
/// # Examples
///
/// ```
/// use tauri::Manager;
///
/// tauri::Builder::default()
/// .setup(|app| {
/// let handler = app.listen("component-loaded", move |event| {
/// println!("app just loaded a component");
/// });
///
/// // stop listening to the event when you do not need it anymore
/// app.unlisten(handler);
///
/// Ok(())
/// });
/// ```
pub fn unlisten(&self, id: EventId) {
self.manager.unlisten(id)
}
/// Listen to an event on this app only once.
///
/// See [`Self::listen`] for more information.
pub fn once<F>(&self, event: impl Into<String>, handler: F)
where
F: FnOnce(Event) + Send + 'static,
{
self.manager.once(event.into(), EventTarget::App, handler)
}
}
};
}

View File

@ -0,0 +1,34 @@
# Tauri Event System Specification
## Emitters
Emitters can emit to any and all listeners.
- `App` and `AppHandle`
- `Window`
- `Webview`
- `WebviewWindow`
- Any type that implements `Manager` trait.
## Emit functions
- `emit`: emits an event to all listeners.
- `emit_to`: emits an event to a specified target.
- `emit_filter`: emits an event to targets based on a filtering callback.
## Listeners
Emitters can emit to any and all listeners.
- `App` and `AppHandle`
- `Window`
- `Webview`
- `WebviewWindow`
- Any type that implements `Manager` trait but is limited to only using `listen_any/once_any`.
## Listen functions
- `listen`: Listens to all events targeting this listener type only.
- `once`: Listens to a single event targeting this listener type only.
- `listen_any` (available only through `Manager` trait): Listens to all events to any target (aka event sniffer).
- `once_any` (available only through `Manager` trait): Listens to a single event to any target (aka event sniffer).

View File

@ -4,51 +4,76 @@
use crate::{Runtime, Webview};
use super::{EmitArgs, Event, EventId};
use super::{EmitArgs, Event, EventId, EventTarget};
use std::{
boxed::Box,
cell::Cell,
collections::HashMap,
collections::{HashMap, HashSet},
sync::{
atomic::{AtomicU32, Ordering},
Arc, Mutex,
},
};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct JsHandler {
target: EventTarget,
id: EventId,
}
/// What to do with the pending handler when resolving it?
enum Pending<R: Runtime> {
enum Pending {
Unlisten(EventId),
Listen(EventId, String, Handler<R>),
Listen {
id: EventId,
event: String,
handler: Handler,
},
Emit(EmitArgs),
}
/// Stored in [`Listeners`] to be called upon when the event that stored it is triggered.
struct Handler<R: Runtime> {
webview: Option<Webview<R>>,
/// Stored in [`Listeners`] to be called upon, when the event that stored it, is triggered.
struct Handler {
target: EventTarget,
callback: Box<dyn Fn(Event) + Send>,
}
impl Handler {
fn new<F: Fn(Event) + Send + 'static>(target: EventTarget, callback: F) -> Self {
Self {
target,
callback: Box::new(callback),
}
}
}
type WebviewLabel = String;
type EventName = String;
/// Holds event handlers and pending event handlers, along with the salts associating them.
struct InnerListeners<R: Runtime> {
handlers: Mutex<HashMap<String, HashMap<EventId, Handler<R>>>>,
pending: Mutex<Vec<Pending<R>>>,
struct InnerListeners {
pending: Mutex<Vec<Pending>>,
handlers: Mutex<HashMap<EventName, HashMap<EventId, Handler>>>,
js_event_listeners: Mutex<HashMap<WebviewLabel, HashMap<EventName, HashSet<JsHandler>>>>,
function_name: &'static str,
listeners_object_name: &'static str,
next_event_id: Arc<AtomicU32>,
}
/// A self-contained event manager.
pub struct Listeners<R: Runtime> {
inner: Arc<InnerListeners<R>>,
#[derive(Clone)]
pub struct Listeners {
inner: Arc<InnerListeners>,
}
impl<R: Runtime> Default for Listeners<R> {
impl Default for Listeners {
fn default() -> Self {
Self {
inner: Arc::new(InnerListeners {
handlers: Mutex::default(),
pending: Mutex::default(),
handlers: Mutex::default(),
js_event_listeners: Mutex::default(),
function_name: "__internal_unstable_listeners_function_id__",
listeners_object_name: "__internal_unstable_listeners_object_id__",
next_event_id: Default::default(),
@ -57,15 +82,7 @@ impl<R: Runtime> Default for Listeners<R> {
}
}
impl<R: Runtime> Clone for Listeners<R> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<R: Runtime> Listeners<R> {
impl Listeners {
pub(crate) fn next_event_id(&self) -> EventId {
self.inner.next_event_id.fetch_add(1, Ordering::Relaxed)
}
@ -81,7 +98,7 @@ impl<R: Runtime> Listeners<R> {
}
/// Insert a pending event action to the queue.
fn insert_pending(&self, action: Pending<R>) {
fn insert_pending(&self, action: Pending) {
self
.inner
.pending
@ -104,9 +121,9 @@ impl<R: Runtime> Listeners<R> {
for action in pending {
match action {
Pending::Unlisten(id) => self.unlisten(id),
Pending::Listen(id, event, handler) => self.listen_with_id(id, event, handler),
Pending::Listen { id, event, handler } => self.listen_with_id(id, event, handler),
Pending::Emit(args) => {
self.emit(&args)?;
self.emit(args)?;
}
}
}
@ -114,9 +131,9 @@ impl<R: Runtime> Listeners<R> {
Ok(())
}
fn listen_with_id(&self, id: EventId, event: String, handler: Handler<R>) {
fn listen_with_id(&self, id: EventId, event: String, handler: Handler) {
match self.inner.handlers.try_lock() {
Err(_) => self.insert_pending(Pending::Listen(id, event, handler)),
Err(_) => self.insert_pending(Pending::Listen { id, event, handler }),
Ok(mut lock) => {
lock.entry(event).or_default().insert(id, handler);
}
@ -127,17 +144,12 @@ impl<R: Runtime> Listeners<R> {
pub(crate) fn listen<F: Fn(Event) + Send + 'static>(
&self,
event: String,
webview: Option<Webview<R>>,
target: EventTarget,
handler: F,
) -> EventId {
let id = self.next_event_id();
let handler = Handler {
webview,
callback: Box::new(handler),
};
let handler = Handler::new(target, handler);
self.listen_with_id(id, event, handler);
id
}
@ -145,18 +157,19 @@ impl<R: Runtime> Listeners<R> {
pub(crate) fn once<F: FnOnce(Event) + Send + 'static>(
&self,
event: String,
webview: Option<Webview<R>>,
target: EventTarget,
handler: F,
) {
let self_ = self.clone();
let handler = Cell::new(Some(handler));
self.listen(event, webview, move |event| {
self_.unlisten(event.id);
self.listen(event, target, move |event| {
let id = event.id;
let handler = handler
.take()
.expect("attempted to call handler more than once");
handler(event)
handler(event);
self_.unlisten(id);
});
}
@ -171,42 +184,27 @@ impl<R: Runtime> Listeners<R> {
}
/// Emits the given event with its payload based on a filter.
pub(crate) fn emit_filter<F>(&self, emit_args: &EmitArgs, filter: Option<F>) -> crate::Result<()>
pub(crate) fn emit_filter<F>(&self, emit_args: EmitArgs, filter: Option<F>) -> crate::Result<()>
where
F: Fn(&Webview<R>) -> bool,
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 = if let Some(filter) = filter {
handlers
let handlers: Vec<_> = match filter {
Some(filter) => handlers
.iter()
.filter(|h| {
h.1
.webview
.as_ref()
.map(|w| {
// clippy sees this as redundant closure but
// fixing it will result in a compiler error
#[allow(clippy::redundant_closure)]
filter(w)
})
.unwrap_or(false)
})
.collect::<Vec<_>>()
} else {
handlers.iter().collect::<Vec<_>>()
.filter(|(_, Handler { target, .. })| *target == EventTarget::Any || filter(target))
.collect(),
None => handlers.iter().collect(),
};
if !handlers.is_empty() {
for (&id, handler) in handlers {
maybe_pending = true;
(handler.callback)(self::Event {
id,
data: emit_args.payload.clone(),
})
maybe_pending = true;
for (&id, Handler { callback, .. }) in handlers {
(callback)(Event::new(id, emit_args.payload.clone()))
}
}
}
@ -221,15 +219,100 @@ impl<R: Runtime> Listeners<R> {
}
/// Emits the given event with its payload.
pub(crate) fn emit(&self, emit_args: &EmitArgs) -> crate::Result<()> {
self.emit_filter(emit_args, None::<&dyn Fn(&Webview<R>) -> bool>)
pub(crate) fn emit(&self, emit_args: EmitArgs) -> crate::Result<()> {
self.emit_filter(emit_args, None::<&dyn Fn(&EventTarget) -> bool>)
}
pub(crate) fn listen_js(
&self,
event: &str,
source_webview_label: &str,
target: EventTarget,
id: EventId,
) {
let mut listeners = self.inner.js_event_listeners.lock().unwrap();
listeners
.entry(source_webview_label.to_string())
.or_default()
.entry(event.to_string())
.or_default()
.insert(JsHandler { id, target });
}
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
});
if handlers.is_empty() {
empty.replace(key.clone());
}
if found {
break 'outer;
}
}
if let Some(key) = &empty {
listeners.remove(key);
}
}
}
pub(crate) fn has_js_listener<F: Fn(&EventTarget) -> bool>(
&self,
event: &str,
filter: F,
) -> bool {
let listeners = self.inner.js_event_listeners.lock().unwrap();
listeners.values().any(|events| {
events
.get(event)
.map(|handlers| handlers.iter().any(|h| filter(&h.target)))
.unwrap_or(false)
})
}
pub(crate) fn try_for_each_js<'a, R, I, F>(
&self,
event: &str,
mut webviews: I,
callback: F,
) -> crate::Result<()>
where
R: Runtime,
I: Iterator<Item = &'a Webview<R>>,
F: Fn(&Webview<R>, &EventTarget) -> crate::Result<()>,
{
let 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)?;
}
}
Ok(())
})
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{event::EventSource, test::MockRuntime};
use crate::event::EventTarget;
use proptest::prelude::*;
// dummy event handler function
@ -243,11 +326,11 @@ mod test {
// check to see if listen() is properly passing keys into the LISTENERS map
#[test]
fn listeners_check_key(e in "[a-z]+") {
let listeners: Listeners<MockRuntime> = Default::default();
let listeners: Listeners = Default::default();
// clone e as the key
let key = e.clone();
// pass e and an dummy func into listen
listeners.listen(e, None, event_fn);
listeners.listen(e, EventTarget::Any, event_fn);
// lock mutex
let l = listeners.inner.handlers.lock().unwrap();
@ -259,11 +342,11 @@ mod test {
// check to see if listen inputs a handler function properly into the LISTENERS map.
#[test]
fn listeners_check_fn(e in "[a-z]+") {
let listeners: Listeners<MockRuntime> = Default::default();
let listeners: Listeners = Default::default();
// clone e as the key
let key = e.clone();
// pass e and an dummy func into listen
listeners.listen(e, None, event_fn);
listeners.listen(e, EventTarget::Any, event_fn);
// lock mutex
let mut l = listeners.inner.handlers.lock().unwrap();
@ -285,14 +368,13 @@ mod test {
// check to see if on_event properly grabs the stored function from listen.
#[test]
fn check_on_event(key in "[a-z]+", d in "[a-z]+") {
let listeners: Listeners<MockRuntime> = Default::default();
let listeners: Listeners = Default::default();
// call listen with key and the event_fn dummy func
listeners.listen(key.clone(), None, event_fn);
listeners.listen(key.clone(), EventTarget::Any, event_fn);
// call on event with key and d.
listeners.emit(&EmitArgs {
listeners.emit(EmitArgs {
event_name: key.clone(),
event: serde_json::to_string(&key).unwrap(),
source: serde_json::to_string(&EventSource::Global).unwrap(),
payload: serde_json::to_string(&d).unwrap()
})?;

View File

@ -4,6 +4,8 @@
mod listener;
pub(crate) mod plugin;
use std::{convert::Infallible, str::FromStr};
pub(crate) use listener::Listeners;
use serde::{Deserialize, Serialize};
@ -24,13 +26,98 @@ pub fn assert_event_name_is_valid(event: &str) {
/// Unique id of an event.
pub type EventId = u32;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
/// Event Target
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
#[serde(tag = "kind")]
#[serde(rename_all = "camelCase")]
pub enum EventSource {
Global,
Window { label: String },
Webview { label: String },
#[non_exhaustive]
pub enum EventTarget {
/// Any and all event targets.
Any,
/// Any [`Window`](crate::Window), [`Webview`](crate::Webview) or [`WebviewWindow`](crate::WebviewWindow) that have this label.
AnyLabel {
/// Target label.
label: String,
},
/// [`App`](crate::App) and [`AppHandle`](crate::AppHandle) targets.
App,
/// [`Window`](crate::Window) target.
Window {
/// window label.
label: String,
},
/// [`Webview`](crate::Webview) target.
Webview {
/// webview label.
label: String,
},
/// [`WebviewWindow`](crate::WebviewWindow) target.
WebviewWindow {
/// webview window label.
label: String,
},
}
impl EventTarget {
/// [`Self::Any`] target.
pub fn any() -> Self {
Self::Any
}
/// [`Self::App`] target.
pub fn app() -> Self {
Self::App
}
/// [`Self::AnyLabel`] target.
pub fn labeled(label: impl Into<String>) -> Self {
Self::AnyLabel {
label: label.into(),
}
}
/// [`Self::Window`] target.
pub fn window(label: impl Into<String>) -> Self {
Self::Window {
label: label.into(),
}
}
/// [`Self::Webview`] target.
pub fn webview(label: impl Into<String>) -> Self {
Self::Webview {
label: label.into(),
}
}
/// [`Self::WebviewWindow`] target.
pub fn webview_window(label: impl Into<String>) -> Self {
Self::WebviewWindow {
label: label.into(),
}
}
}
impl<T: AsRef<str>> From<T> for EventTarget {
fn from(value: T) -> Self {
Self::AnyLabel {
label: value.as_ref().to_string(),
}
}
}
impl FromStr for EventTarget {
type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self::AnyLabel {
label: s.to_string(),
})
}
}
/// Serialized emit arguments.
@ -40,20 +127,17 @@ pub struct EmitArgs {
pub event_name: String,
/// Serialized event name.
pub event: String,
/// Serialized [`EventSource`].
pub source: String,
/// Serialized payload.
pub payload: String,
}
impl EmitArgs {
pub fn from<S: Serialize>(event: &str, source: &EventSource, payload: S) -> crate::Result<Self> {
pub fn new<S: Serialize>(event: &str, payload: S) -> crate::Result<Self> {
#[cfg(feature = "tracing")]
let _span = tracing::debug_span!("window::emit::serialize").entered();
Ok(EmitArgs {
event_name: event.into(),
event: serde_json::to_string(event)?,
source: serde_json::to_string(source)?,
payload: serde_json::to_string(&payload)?,
})
}
@ -67,6 +151,10 @@ pub struct Event {
}
impl Event {
fn new(id: EventId, data: String) -> Self {
Self { id, data }
}
/// The [`EventId`] of the handler that was triggered.
pub fn id(&self) -> EventId {
self.id
@ -78,11 +166,11 @@ impl Event {
}
}
pub fn listen_js(
pub fn listen_js_script(
listeners_object_name: &str,
serialized_target: &str,
event: &str,
event_id: EventId,
serialized_source: &str,
handler: &str,
) -> String {
format!(
@ -91,34 +179,42 @@ pub fn listen_js(
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: [] }});
if (window['{listeners}']['{event}'] === void 0) {{
Object.defineProperty(window['{listeners}'], '{event}', {{ value: [] }});
}}
const eventListeners = window['{listeners}'][{event}]
const eventListeners = window['{listeners}']['{event}']
const listener = {{
id: {event_id},
source: {source},
target: {target},
handler: {handler}
}};
eventListeners.push(listener);
}})()
",
listeners = listeners_object_name,
source = serialized_source,
target = serialized_target,
)
}
pub fn emit_js(event_emit_function_name: &str, emit_args: &EmitArgs) -> crate::Result<String> {
pub fn emit_js_script(
event_emit_function_name: &str,
emit_args: &EmitArgs,
serialized_target: &str,
) -> crate::Result<String> {
Ok(format!(
"(function () {{ const fn = window['{}']; fn && fn({{event: {}, source: {}, payload: {}}}) }})()",
"(function () {{ const fn = window['{}']; fn && fn({{event: {}, payload: {}}}, {target}) }})()",
event_emit_function_name,
emit_args.event,
emit_args.source,
emit_args.payload
emit_args.payload,
target = serialized_target,
))
}
pub fn unlisten_js(listeners_object_name: &str, event_name: &str, event_id: EventId) -> String {
pub fn unlisten_js_script(
listeners_object_name: &str,
event_name: &str,
event_id: EventId,
) -> String {
format!(
"
(function () {{
@ -138,12 +234,11 @@ pub fn event_initialization_script(function: &str, listeners: &str) -> String {
format!(
"
Object.defineProperty(window, '{function}', {{
value: function (eventData) {{
value: function (eventData, target) {{
const listeners = (window['{listeners}'] && window['{listeners}'][eventData.event]) || []
for (let i = listeners.length - 1; i >= 0; i--) {{
const listener = listeners[i]
if (listener.source.kind === 'global' || eventData.source.kind === 'global' || listener.source.kind === 'window' || eventData.source.kind === 'window' || listener.source.label === eventData.source.label) {{
if ((listener.target.kind === 'Global' && target.kind === 'Global') || (listener.target.kind === target.kind && listener.target.label === target.label)) {{
eventData.id = listener.id
listener.handler(eventData)
}}

View File

@ -2,21 +2,24 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use std::ops::Deref;
use serde::{Deserialize, Deserializer};
use serde_json::Value as JsonValue;
use tauri_runtime::window::is_label_valid;
use crate::plugin::{Builder, TauriPlugin};
use crate::sealed::ManagerBase;
use crate::{command, ipc::CallbackFn, EventId, Manager, Result, Runtime};
use crate::{AppHandle, Webview};
use super::{is_event_name_valid, EventSource};
use super::{is_event_name_valid, EventTarget};
pub struct EventName(String);
impl AsRef<str> for EventName {
fn as_ref(&self) -> &str {
impl Deref for EventName {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
@ -63,21 +66,12 @@ impl<'de> Deserialize<'de> for WebviewLabel {
#[command(root = "crate")]
pub fn listen<R: Runtime>(
app: AppHandle<R>,
webview: Webview<R>,
event: EventName,
webview_label: Option<WebviewLabel>,
target: EventTarget,
handler: CallbackFn,
) -> Result<EventId> {
if let Some(l) = webview_label {
app
.manager()
.get_webview(&l.0)
.ok_or(crate::Error::WebviewNotFound)?
.listen_js(EventSource::Webview { label: l.0 }, event.0, handler)
} else {
webview.listen_js(EventSource::Global, event.0, handler)
}
webview.listen_js(&event, target, handler)
}
#[command(root = "crate")]
@ -86,28 +80,31 @@ pub fn unlisten<R: Runtime>(
event: EventName,
event_id: EventId,
) -> Result<()> {
webview.unlisten_js(event.as_ref(), event_id)
webview.unlisten_js(&event, event_id)
}
#[command(root = "crate")]
pub fn emit<R: Runtime>(
app: AppHandle<R>,
webview: Webview<R>,
event: EventName,
target: Option<EventSource>,
payload: Option<JsonValue>,
) -> Result<()> {
let target = target.unwrap_or(EventSource::Global);
match target {
EventSource::Global => app.emit(&event.0, payload),
EventSource::Webview { label } => webview.emit_to(&label, &event.0, payload),
EventSource::Window { label } => webview.window().emit_to(&label, &event.0, payload),
}
app.emit(&event, payload)
}
#[command(root = "crate")]
pub fn emit_to<R: Runtime>(
app: AppHandle<R>,
target: EventTarget,
event: EventName,
payload: Option<JsonValue>,
) -> Result<()> {
app.emit_to(target, &event, payload)
}
/// Initializes the event plugin.
pub(crate) fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("event")
.invoke_handler(crate::generate_handler![listen, unlisten, emit,])
.invoke_handler(crate::generate_handler![listen, unlisten, emit, emit_to])
.build()
}

View File

@ -67,7 +67,6 @@ pub use cocoa;
#[doc(hidden)]
pub use embed_plist;
pub use error::{Error, Result};
use event::EventSource;
pub use resources::{Resource, ResourceId, ResourceTable};
#[cfg(target_os = "ios")]
#[doc(hidden)]
@ -203,7 +202,7 @@ pub use runtime::ActivationPolicy;
#[cfg(target_os = "macos")]
pub use self::utils::TitleBarStyle;
pub use self::event::{Event, EventId};
pub use self::event::{Event, EventId, EventTarget};
pub use {
self::app::{App, AppHandle, AssetResolver, Builder, CloseRequestApi, RunEvent, WindowEvent},
self::manager::Asset,
@ -589,7 +588,7 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
self.manager().package_info()
}
/// Listen to an event emitted on any window.
/// Listen to an emitted event to any [target](EventTarget).
///
/// # Examples
/// ```
@ -603,18 +602,20 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
///
/// tauri::Builder::default()
/// .setup(|app| {
/// app.listen_global("synchronized", |event| {
/// app.listen_any("synchronized", |event| {
/// println!("app is in sync");
/// });
/// Ok(())
/// })
/// .invoke_handler(tauri::generate_handler![synchronize]);
/// ```
fn listen_global<F>(&self, event: impl Into<String>, handler: F) -> EventId
fn listen_any<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: Fn(Event) + Send + 'static,
{
self.manager().listen(event.into(), None, handler)
self
.manager()
.listen(event.into(), EventTarget::Any, handler)
}
/// Remove an event listener.
@ -626,7 +627,7 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
/// tauri::Builder::default()
/// .setup(|app| {
/// let handle = app.handle().clone();
/// let handler = app.listen_global("ready", move |event| {
/// let handler = app.listen_any("ready", move |event| {
/// println!("app is ready");
///
/// // we no longer need to listen to the event
@ -645,19 +646,17 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
self.manager().unlisten(id)
}
/// Listen to a global event only once.
/// Listens once to an emitted event to any [target](EventTarget) .
///
/// See [`Self::listen_global`] for more information.
fn once_global<F>(&self, event: impl Into<String>, handler: F)
/// See [`Self::listen_any`] for more information.
fn once_any<F>(&self, event: impl Into<String>, handler: F)
where
F: FnOnce(Event) + Send + 'static,
{
self.manager().once(event.into(), None, handler)
self.manager().once(event.into(), EventTarget::Any, handler)
}
/// Emits an event to all webviews.
///
/// If using [`Window`] to emit the event, it will be used as the source.
/// Emits an event to all [targets](EventTarget).
///
/// # Examples
/// ```
@ -674,50 +673,76 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
tracing::instrument("app::emit", skip(self, payload))
)]
fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> Result<()> {
self.manager().emit(event, EventSource::Global, payload)
self.manager().emit(event, payload)
}
/// Emits an event to the webview with the specified label.
///
/// If using [`Window`] to emit the event, it will be used as the source.
/// Emits an event to all [targets](EventTarget) matching the given target.
///
/// # Examples
/// ```
/// use tauri::Manager;
/// use tauri::{Manager, EventTarget};
///
/// #[tauri::command]
/// fn download(app: tauri::AppHandle) {
/// for i in 1..100 {
/// std::thread::sleep(std::time::Duration::from_millis(150));
/// // emit a download progress event to the updater window
/// app.emit_to("updater", "download-progress", i);
/// // emit a download progress event to all listeners
/// app.emit_to(EventTarget::any(), "download-progress", i);
/// // emit an event to listeners that used App::listen or AppHandle::listen
/// app.emit_to(EventTarget::app(), "download-progress", i);
/// // emit an event to any webview/window/webviewWindow matching the given label
/// app.emit_to("updater", "download-progress", i); // similar to using EventTarget::labeled
/// app.emit_to(EventTarget::labeled("updater"), "download-progress", i);
/// // emit an event to listeners that used WebviewWindow::listen
/// app.emit_to(EventTarget::webview_window("updater"), "download-progress", i);
/// }
/// }
/// ```
#[cfg_attr(
feature = "tracing",
tracing::instrument("app::emit::to", skip(self, payload))
tracing::instrument("app::emit::to", skip(self, target, payload), fields(target))
)]
fn emit_to<S: Serialize + Clone>(&self, label: &str, event: &str, payload: S) -> Result<()> {
self
.manager()
.emit_filter(event, EventSource::Global, payload, |w| label == w.label())
fn emit_to<I, S>(&self, target: I, event: &str, payload: S) -> Result<()>
where
I: Into<EventTarget>,
S: Serialize + Clone,
{
let target = target.into();
#[cfg(feature = "tracing")]
tracing::Span::current().record("target", format!("{target:?}"));
self.manager().emit_filter(event, payload, |s| match s {
t @ EventTarget::Window { label }
| t @ EventTarget::Webview { label }
| t @ EventTarget::WebviewWindow { label } => {
if let EventTarget::AnyLabel {
label: target_label,
} = &target
{
label == target_label
} else {
t == &target
}
}
t => t == &target,
})
}
/// Emits an event to specific webviews based on a filter.
///
/// If using [`Window`] to emit the event, it will be used as the source.
/// Emits an event to all [targets](EventTarget) based on the given filter.
///
/// # Examples
/// ```
/// use tauri::Manager;
/// use tauri::{Manager, EventTarget};
///
/// #[tauri::command]
/// fn download(app: tauri::AppHandle) {
/// for i in 1..100 {
/// std::thread::sleep(std::time::Duration::from_millis(150));
/// // emit a download progress event to the updater window
/// app.emit_filter("download-progress", i, |w| w.label() == "main" );
/// app.emit_filter("download-progress", i, |t| match t {
/// EventTarget::WebviewWindow { label } => label == "main",
/// _ => false,
/// });
/// }
/// }
/// ```
@ -728,11 +753,9 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> Result<()>
where
S: Serialize + Clone,
F: Fn(&Webview<R>) -> bool,
F: Fn(&EventTarget) -> bool,
{
self
.manager()
.emit_filter(event, EventSource::Global, payload, filter)
self.manager().emit_filter(event, payload, filter)
}
/// Fetch a single window from the manager.

View File

@ -22,7 +22,7 @@ use tauri_utils::{
use crate::{
app::{AppHandle, GlobalWindowEventListener, OnPageLoad},
event::{assert_event_name_is_valid, Event, EventId, EventSource, Listeners},
event::{assert_event_name_is_valid, Event, EventId, EventTarget, Listeners},
ipc::{Invoke, InvokeHandler, InvokeResponder, RuntimeAuthority},
plugin::PluginStore,
utils::{
@ -188,7 +188,7 @@ pub struct AppManager<R: Runtime> {
pub menu: menu::MenuManager<R>,
pub(crate) plugins: Mutex<PluginStore<R>>,
pub listeners: Listeners<R>,
pub listeners: Listeners,
pub state: Arc<StateManager>,
pub config: Config,
pub assets: Box<dyn Assets>,
@ -418,7 +418,7 @@ impl<R: Runtime> AppManager<R> {
}
}
pub(crate) fn listeners(&self) -> &Listeners<R> {
pub(crate) fn listeners(&self) -> &Listeners {
&self.listeners
}
@ -453,11 +453,11 @@ impl<R: Runtime> AppManager<R> {
pub fn listen<F: Fn(Event) + Send + 'static>(
&self,
event: String,
window: Option<Webview<R>>,
target: EventTarget,
handler: F,
) -> EventId {
assert_event_name_is_valid(&event);
self.listeners().listen(event, window, handler)
self.listeners().listen(event, target, handler)
}
pub fn unlisten(&self, id: EventId) {
@ -467,65 +467,59 @@ impl<R: Runtime> AppManager<R> {
pub fn once<F: FnOnce(Event) + Send + 'static>(
&self,
event: String,
webview: Option<String>,
target: EventTarget,
handler: F,
) {
assert_event_name_is_valid(&event);
self
.listeners()
.once(event, webview.and_then(|w| self.get_webview(&w)), handler)
self.listeners().once(event, target, handler)
}
pub fn emit_filter<S, F>(
&self,
event: &str,
source: EventSource,
payload: S,
filter: F,
) -> crate::Result<()>
pub fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> crate::Result<()>
where
S: Serialize + Clone,
F: Fn(&Webview<R>) -> bool,
F: Fn(&EventTarget) -> bool,
{
assert_event_name_is_valid(event);
#[cfg(feature = "tracing")]
let _span = tracing::debug_span!("emit::run").entered();
let emit_args = EmitArgs::from(event, &source, payload)?;
let emit_args = EmitArgs::new(event, payload)?;
self
.webview
.webviews_lock()
.values()
.filter(|w| w.has_js_listener(&source, event))
.filter(|w| filter(w))
.try_for_each(|webview| webview.emit_js(&emit_args))?;
let listeners = self.listeners();
self.listeners().emit_filter(&emit_args, Some(filter))?;
listeners.try_for_each_js(
event,
self.webview.webviews_lock().values(),
|webview, target| {
if filter(target) {
webview.emit_js(&emit_args, target)
} else {
Ok(())
}
},
)?;
listeners.emit_filter(emit_args, Some(filter))?;
Ok(())
}
pub fn emit<S: Serialize + Clone>(
&self,
event: &str,
source: EventSource,
payload: S,
) -> crate::Result<()> {
pub fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
assert_event_name_is_valid(event);
#[cfg(feature = "tracing")]
let _span = tracing::debug_span!("emit::run").entered();
let emit_args = EmitArgs::from(event, &source, payload)?;
let emit_args = EmitArgs::new(event, payload)?;
self
.webview
.webviews_lock()
.values()
.filter(|w| w.has_js_listener(&source, event))
.try_for_each(|window| window.emit_js(&emit_args))?;
let listeners = self.listeners();
self.listeners().emit(&emit_args)?;
listeners.try_for_each_js(
event,
self.webview.webviews_lock().values(),
|webview, target| webview.emit_js(&emit_args, target),
)?;
listeners.emit(emit_args)?;
Ok(())
}
@ -618,17 +612,25 @@ mod test {
};
use crate::{
event::EventTarget,
generate_context,
plugin::PluginStore,
test::{mock_app, MockRuntime},
App, Manager, StateManager, WebviewWindow, WebviewWindowBuilder, Wry,
webview::WebviewBuilder,
window::WindowBuilder,
App, Manager, StateManager, Webview, WebviewWindow, WebviewWindowBuilder, Window, Wry,
};
use super::AppManager;
const APP_LISTEN_ID: &str = "App::listen";
const APP_LISTEN_ANY_ID: &str = "App::listen_any";
const WINDOW_LISTEN_ID: &str = "Window::listen";
const WINDOW_LISTEN_GLOBAL_ID: &str = "Window::listen_global";
const APP_LISTEN_GLOBAL_ID: &str = "App::listen_global";
const WINDOW_LISTEN_ANY_ID: &str = "Window::listen_any";
const WEBVIEW_LISTEN_ID: &str = "Webview::listen";
const WEBVIEW_LISTEN_ANY_ID: &str = "Webview::listen_any";
const WEBVIEW_WINDOW_LISTEN_ID: &str = "WebviewWindow::listen";
const WEBVIEW_WINDOW_LISTEN_ANY_ID: &str = "WebviewWindow::listen_any";
const TEST_EVENT_NAME: &str = "event";
#[test]
@ -664,147 +666,215 @@ mod test {
struct EventSetup {
app: App<MockRuntime>,
webview: WebviewWindow<MockRuntime>,
window: Window<MockRuntime>,
webview: Webview<MockRuntime>,
webview_window: WebviewWindow<MockRuntime>,
tx: Sender<(&'static str, String)>,
rx: Receiver<(&'static str, String)>,
}
fn setup_events() -> EventSetup {
fn setup_events(setup_any: bool) -> EventSetup {
let app = mock_app();
let webview = WebviewWindowBuilder::new(&app, "main", Default::default())
let window = WindowBuilder::new(&app, "main-window").build().unwrap();
let webview = window
.add_child(
WebviewBuilder::new("main-webview", Default::default()),
crate::LogicalPosition::new(0, 0),
window.inner_size().unwrap(),
)
.unwrap();
let webview_window = WebviewWindowBuilder::new(&app, "main-webview-window", Default::default())
.build()
.unwrap();
let (tx, rx) = channel();
let tx_ = tx.clone();
webview.listen(TEST_EVENT_NAME, move |evt| {
tx_
.send((
WINDOW_LISTEN_ID,
serde_json::from_str::<String>(evt.payload()).unwrap(),
))
.unwrap();
});
macro_rules! setup_listener {
($type:ident, $id:ident, $any_id:ident) => {
let tx_ = tx.clone();
$type.listen(TEST_EVENT_NAME, move |evt| {
tx_
.send(($id, serde_json::from_str::<String>(evt.payload()).unwrap()))
.unwrap();
});
let tx_ = tx.clone();
webview.listen_global(TEST_EVENT_NAME, move |evt| {
tx_
.send((
WINDOW_LISTEN_GLOBAL_ID,
serde_json::from_str::<String>(evt.payload()).unwrap(),
))
.unwrap();
});
if setup_any {
let tx_ = tx.clone();
$type.listen_any(TEST_EVENT_NAME, move |evt| {
tx_
.send((
$any_id,
serde_json::from_str::<String>(evt.payload()).unwrap(),
))
.unwrap();
});
}
};
}
let tx_ = tx.clone();
app.listen_global(TEST_EVENT_NAME, move |evt| {
tx_
.send((
APP_LISTEN_GLOBAL_ID,
serde_json::from_str::<String>(evt.payload()).unwrap(),
))
.unwrap();
});
setup_listener!(app, APP_LISTEN_ID, APP_LISTEN_ANY_ID);
setup_listener!(window, WINDOW_LISTEN_ID, WINDOW_LISTEN_ANY_ID);
setup_listener!(webview, WEBVIEW_LISTEN_ID, WEBVIEW_LISTEN_ANY_ID);
setup_listener!(
webview_window,
WEBVIEW_WINDOW_LISTEN_ID,
WEBVIEW_WINDOW_LISTEN_ANY_ID
);
EventSetup {
app,
window,
webview,
webview_window,
tx,
rx,
}
}
fn assert_events(received: &[&str], expected: &[&str]) {
fn assert_events(kind: &str, received: &[&str], expected: &[&str]) {
for e in expected {
assert!(received.contains(e), "{e} did not receive global event");
assert!(received.contains(e), "{e} did not receive `{kind}` event");
}
assert_eq!(
received.len(),
expected.len(),
"received {:?} events but expected {:?}",
"received {:?} `{kind}` events but expected {:?}",
received,
expected
);
}
#[test]
fn app_global_events() {
fn emit() {
let EventSetup {
app,
webview: _,
window,
webview,
webview_window,
tx: _,
rx,
} = setup_events();
} = setup_events(true);
run_emit_test("emit (app)", app, &rx);
run_emit_test("emit (window)", window, &rx);
run_emit_test("emit (webview)", webview, &rx);
run_emit_test("emit (webview_window)", webview_window, &rx);
}
fn run_emit_test<M: Manager<MockRuntime>>(kind: &str, m: M, rx: &Receiver<(&str, String)>) {
let mut received = Vec::new();
let payload = "global-payload";
app.emit(TEST_EVENT_NAME, payload).unwrap();
m.emit(TEST_EVENT_NAME, payload).unwrap();
while let Ok((source, p)) = rx.recv_timeout(Duration::from_secs(1)) {
assert_eq!(p, payload);
received.push(source);
}
assert_events(
kind,
&received,
&[
APP_LISTEN_ID,
APP_LISTEN_ANY_ID,
WINDOW_LISTEN_ID,
WINDOW_LISTEN_GLOBAL_ID,
APP_LISTEN_GLOBAL_ID,
WINDOW_LISTEN_ANY_ID,
WEBVIEW_LISTEN_ID,
WEBVIEW_LISTEN_ANY_ID,
WEBVIEW_WINDOW_LISTEN_ID,
WEBVIEW_WINDOW_LISTEN_ANY_ID,
],
);
}
#[test]
fn window_global_events() {
let EventSetup {
app: _,
webview,
tx: _,
rx,
} = setup_events();
let mut received = Vec::new();
let payload = "global-payload";
webview.emit(TEST_EVENT_NAME, payload).unwrap();
while let Ok((source, p)) = rx.recv_timeout(Duration::from_secs(1)) {
assert_eq!(p, payload);
received.push(source);
}
assert_events(
&received,
&[
WINDOW_LISTEN_ID,
WINDOW_LISTEN_GLOBAL_ID,
APP_LISTEN_GLOBAL_ID,
],
);
}
#[test]
fn window_local_events() {
fn emit_to() {
let EventSetup {
app,
window,
webview,
webview_window,
tx,
rx,
} = setup_events();
} = setup_events(false);
run_emit_to_test(
"emit_to (App)",
&app,
&window,
&webview,
&webview_window,
tx.clone(),
&rx,
);
run_emit_to_test(
"emit_to (window)",
&window,
&window,
&webview,
&webview_window,
tx.clone(),
&rx,
);
run_emit_to_test(
"emit_to (webview)",
&webview,
&window,
&webview,
&webview_window,
tx.clone(),
&rx,
);
run_emit_to_test(
"emit_to (webview_window)",
&webview_window,
&window,
&webview,
&webview_window,
tx.clone(),
&rx,
);
}
fn run_emit_to_test<M: Manager<MockRuntime>>(
kind: &str,
m: &M,
window: &Window<MockRuntime>,
webview: &Webview<MockRuntime>,
webview_window: &WebviewWindow<MockRuntime>,
tx: Sender<(&'static str, String)>,
rx: &Receiver<(&'static str, String)>,
) {
let mut received = Vec::new();
let payload = "global-payload";
webview
.emit_to(webview.label(), TEST_EVENT_NAME, payload)
.unwrap();
while let Ok((source, p)) = rx.recv_timeout(Duration::from_secs(1)) {
assert_eq!(p, payload);
received.push(source);
}
assert_events(&received, &[WINDOW_LISTEN_ID]);
received.clear();
macro_rules! test_target {
($target:expr, $id:ident) => {
m.emit_to($target, TEST_EVENT_NAME, payload).unwrap();
while let Ok((source, p)) = rx.recv_timeout(Duration::from_secs(1)) {
assert_eq!(p, payload);
received.push(source);
}
assert_events(kind, &received, &[$id]);
received.clear();
};
}
test_target!(EventTarget::App, APP_LISTEN_ID);
test_target!(window.label(), WINDOW_LISTEN_ID);
test_target!(webview.label(), WEBVIEW_LISTEN_ID);
test_target!(webview_window.label(), WEBVIEW_WINDOW_LISTEN_ID);
let other_webview_listen_id = "OtherWebview::listen";
let other_webview = WebviewWindowBuilder::new(&app, "other", Default::default())
.build()
.unwrap();
let other_webview = WebviewWindowBuilder::new(
window,
kind.replace(['(', ')', ' '], ""),
Default::default(),
)
.build()
.unwrap();
other_webview.listen(TEST_EVENT_NAME, move |evt| {
tx.send((
@ -813,13 +883,12 @@ mod test {
))
.unwrap();
});
webview
.emit_to(other_webview.label(), TEST_EVENT_NAME, payload)
m.emit_to(other_webview.label(), TEST_EVENT_NAME, payload)
.unwrap();
while let Ok((source, p)) = rx.recv_timeout(Duration::from_secs(1)) {
assert_eq!(p, payload);
received.push(source);
}
assert_events(&received, &[other_webview_listen_id]);
assert_events("emit_to", &received, &[other_webview_listen_id]);
}
}

View File

@ -59,6 +59,7 @@ pub struct UriSchemeProtocol<R: Runtime> {
}
#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WebviewLabelDef {
pub window_label: String,
pub label: String,
@ -168,7 +169,7 @@ impl<R: Runtime> WebviewManager<R> {
Object.defineProperty(window.__TAURI_INTERNALS__, 'metadata', {{
value: {{
windows: {window_labels_array}.map(function (label) {{ return {{ label: label }} }}),
webviews: {webview_labels_array}.map(function (label) {{ return {{ label: label }} }}),
webviews: {webview_labels_array},
currentWindow: {{ label: {current_window_label} }},
currentWebview: {{ label: {current_webview_label} }}
}}

View File

@ -19,8 +19,8 @@ use tauri_runtime::{
};
use crate::{
app::GlobalWindowEventListener, event::EventSource, AppHandle, EventLoopMessage, Icon, Manager,
Runtime, Scopes, Window, WindowEvent,
app::GlobalWindowEventListener, sealed::ManagerBase, AppHandle, EventLoopMessage, EventTarget,
Icon, Manager, Runtime, Scopes, Window, WindowEvent,
};
use super::AppManager;
@ -145,14 +145,15 @@ fn on_window_event<R: Runtime>(
WindowEvent::Resized(size) => window.emit(WINDOW_RESIZED_EVENT, size)?,
WindowEvent::Moved(position) => window.emit(WINDOW_MOVED_EVENT, position)?,
WindowEvent::CloseRequested { api } => {
if window.webviews().iter().any(|w| {
w.has_js_listener(
&EventSource::Window {
label: window.label().into(),
},
WINDOW_CLOSE_REQUESTED_EVENT,
)
}) {
let listeners = window.manager().listeners();
let has_js_listener =
listeners.has_js_listener(WINDOW_CLOSE_REQUESTED_EVENT, |target| match target {
EventTarget::Window { label } | EventTarget::WebviewWindow { label } => {
label == window.label()
}
_ => false,
});
if has_js_listener {
api.prevent_close();
}
window.emit(WINDOW_CLOSE_REQUESTED_EVENT, ())?;

View File

@ -27,7 +27,7 @@ pub use url::Url;
use crate::{
app::UriSchemeResponder,
event::{EmitArgs, EventSource},
event::{EmitArgs, EventTarget},
ipc::{
CallbackFn, CommandArg, CommandItem, Invoke, InvokeBody, InvokeError, InvokeMessage,
InvokeResolver, Origin, OwnedInvokeResponder,
@ -39,7 +39,6 @@ use crate::{
use std::{
borrow::Cow,
collections::{HashMap, HashSet},
hash::{Hash, Hasher},
path::PathBuf,
sync::{Arc, Mutex},
@ -110,34 +109,6 @@ impl<'a> PageLoadPayload<'a> {
}
}
/// Key for a JS event listener.
#[derive(Debug, Clone, PartialEq, Eq)]
struct JsEventListenerKey {
/// The source.
pub source: EventSource,
/// The event name.
pub event: String,
}
impl Hash for JsEventListenerKey {
fn hash<H: Hasher>(&self, state: &mut H) {
self.event.hash(state);
match &self.source {
EventSource::Global => {
"global".hash(state);
}
EventSource::Webview { label } => {
"webview".hash(state);
label.hash(state);
}
EventSource::Window { label } => {
"window".hash(state);
label.hash(state);
}
}
}
}
/// The IPC invoke request.
#[derive(Debug)]
pub struct InvokeRequest {
@ -236,12 +207,12 @@ impl PlatformWebview {
macro_rules! unstable_struct {
(#[doc = $doc:expr] $($tokens:tt)*) => {
#[cfg(feature = "unstable")]
#[cfg(any(test, feature = "unstable"))]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
#[doc = $doc]
pub $($tokens)*
#[cfg(not(feature = "unstable"))]
#[cfg(not(any(test, feature = "unstable")))]
pub(crate) $($tokens)*
}
}
@ -660,11 +631,13 @@ tauri::Builder::default()
app_manager.emit_filter(
"tauri://webview-created",
EventSource::Global,
Some(CreatedEvent {
label: webview.label().into(),
}),
|w| w != &webview,
|s| match s {
EventTarget::Webview { label } => label == webview.label(),
_ => false,
},
)?;
Ok(webview)
@ -811,7 +784,6 @@ pub struct Webview<R: Runtime> {
pub(crate) window: Window<R>,
/// The webview created by the runtime.
pub(crate) webview: DetachedWebview<EventLoopMessage, R>,
js_event_listeners: Arc<Mutex<HashMap<JsEventListenerKey, HashSet<EventId>>>>,
}
impl<R: Runtime> std::fmt::Debug for Webview<R> {
@ -819,7 +791,6 @@ impl<R: Runtime> std::fmt::Debug for Webview<R> {
f.debug_struct("Window")
.field("window", &self.window)
.field("webview", &self.webview)
.field("js_event_listeners", &self.js_event_listeners)
.finish()
}
}
@ -829,7 +800,6 @@ impl<R: Runtime> Clone for Webview<R> {
Self {
window: self.window.clone(),
webview: self.webview.clone(),
js_event_listeners: self.js_event_listeners.clone(),
}
}
}
@ -853,11 +823,7 @@ impl<R: Runtime> PartialEq for Webview<R> {
impl<R: Runtime> Webview<R> {
/// Create a new webview that is attached to the window.
pub(crate) fn new(window: Window<R>, webview: DetachedWebview<EventLoopMessage, R>) -> Self {
Self {
window,
webview,
js_event_listeners: Default::default(),
}
Self { window, webview }
}
/// Initializes a webview builder with the given window label and URL to load on the webview.
@ -1236,107 +1202,51 @@ fn main() {
/// Register a JS event listener and return its identifier.
pub(crate) fn listen_js(
&self,
source: EventSource,
event: String,
event: &str,
target: EventTarget,
handler: CallbackFn,
) -> crate::Result<EventId> {
let event_id = self.manager().listeners().next_event_id();
let listeners = self.manager().listeners();
self.eval(&crate::event::listen_js(
self.manager().listeners().listeners_object_name(),
&format!("'{}'", event),
event_id,
&serde_json::to_string(&source)?,
let id = listeners.next_event_id();
self.eval(&crate::event::listen_js_script(
listeners.listeners_object_name(),
&serde_json::to_string(&target)?,
event,
id,
&format!("window['_{}']", handler.0),
))?;
self
.js_event_listeners
.lock()
.unwrap()
.entry(JsEventListenerKey { source, event })
.or_default()
.insert(event_id);
listeners.listen_js(event, self.label(), target, id);
Ok(event_id)
Ok(id)
}
/// Unregister a JS event listener.
pub(crate) fn unlisten_js(&self, event: &str, id: EventId) -> crate::Result<()> {
self.eval(&crate::event::unlisten_js(
self.manager().listeners().listeners_object_name(),
let listeners = self.manager().listeners();
self.eval(&crate::event::unlisten_js_script(
listeners.listeners_object_name(),
event,
id,
))?;
let mut empty = None;
let mut js_listeners = self.js_event_listeners.lock().unwrap();
let iter = js_listeners.iter_mut();
for (key, ids) in iter {
if ids.contains(&id) {
ids.remove(&id);
if ids.is_empty() {
empty.replace(key.clone());
}
break;
}
}
if let Some(key) = empty {
js_listeners.remove(&key);
}
listeners.unlisten_js(id);
Ok(())
}
pub(crate) fn emit_js(&self, emit_args: &EmitArgs) -> crate::Result<()> {
self.eval(&crate::event::emit_js(
pub(crate) fn emit_js(&self, emit_args: &EmitArgs, target: &EventTarget) -> crate::Result<()> {
self.eval(&crate::event::emit_js_script(
self.manager().listeners().function_name(),
emit_args,
&serde_json::to_string(target)?,
)?)?;
Ok(())
}
/// Whether this webview registered a listener to an event from the given source and event name.
pub(crate) fn has_js_listener(&self, source: &EventSource, event: &str) -> bool {
let listeners = self.js_event_listeners.lock().unwrap();
match source {
// for global events, any listener is triggered
EventSource::Global => listeners.keys().any(|k| k.event == event),
// if the window matches this webview's window,
// the event is delivered as long as it listens to the event name
EventSource::Window { label } if label == self.window.label() => {
let event = event.to_string();
// webview-specific event is also triggered on global events, so we check that
listeners.contains_key(&JsEventListenerKey {
source: source.clone(),
event: event.clone(),
}) || listeners.contains_key(&JsEventListenerKey {
source: EventSource::Webview {
label: label.clone(),
},
event: event.clone(),
}) || listeners.contains_key(&JsEventListenerKey {
source: EventSource::Global,
event,
})
}
_ => {
let event = event.to_string();
// webview-specific event is also triggered on global events, so we check that
listeners.contains_key(&JsEventListenerKey {
source: source.clone(),
event: event.clone(),
}) || listeners.contains_key(&JsEventListenerKey {
source: EventSource::Global,
event,
})
}
}
}
/// Opens the developer tools window (Web Inspector).
/// The devtools is only enabled on debug builds or with the `devtools` feature flag.
///
@ -1473,13 +1383,16 @@ tauri::Builder::default()
where
F: Fn(Event) + Send + 'static,
{
self
.window
.manager
.listen(event.into(), Some(self.clone()), handler)
self.window.manager.listen(
event.into(),
EventTarget::Webview {
label: self.label().to_string(),
},
handler,
)
}
/// Unlisten to an event on this window.
/// Unlisten to an event on this webview.
///
/// # Examples
#[cfg_attr(
@ -1519,55 +1432,18 @@ tauri::Builder::default()
where
F: FnOnce(Event) + Send + 'static,
{
let label = self.webview.label.clone();
self.window.manager.once(event.into(), Some(label), handler)
}
}
impl<R: Runtime> Manager<R> for Webview<R> {
fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
self.manager().emit(
event,
EventSource::Webview {
self.window.manager.once(
event.into(),
EventTarget::Webview {
label: self.label().to_string(),
},
payload,
)?;
Ok(())
}
fn emit_to<S: Serialize + Clone>(
&self,
label: &str,
event: &str,
payload: S,
) -> crate::Result<()> {
self.manager().emit_filter(
event,
EventSource::Webview {
label: self.label().to_string(),
},
payload,
|w| label == w.label(),
)
}
fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> crate::Result<()>
where
S: Serialize + Clone,
F: Fn(&Webview<R>) -> bool,
{
self.manager().emit_filter(
event,
EventSource::Webview {
label: self.label().to_string(),
},
payload,
filter,
handler,
)
}
}
impl<R: Runtime> Manager<R> for Webview<R> {}
impl<R: Runtime> ManagerBase<R> for Webview<R> {
fn manager(&self) -> &AppManager<R> {
&self.window.manager

View File

@ -6,6 +6,11 @@
use std::{borrow::Cow, path::PathBuf, sync::Arc};
use crate::{
event::EventTarget,
runtime::window::dpi::{PhysicalPosition, PhysicalSize},
window::Monitor,
};
#[cfg(desktop)]
use crate::{
menu::{ContextMenu, Menu},
@ -18,11 +23,6 @@ use crate::{
},
Icon,
};
use crate::{
runtime::window::dpi::{PhysicalPosition, PhysicalSize},
window::Monitor,
};
use serde::Serialize;
use tauri_utils::config::{WebviewUrl, WindowConfig};
use url::Url;
@ -861,24 +861,6 @@ unsafe impl<R: Runtime> raw_window_handle::HasRawWindowHandle for WebviewWindow<
}
}
impl<R: Runtime> ManagerBase<R> for WebviewWindow<R> {
fn manager(&self) -> &AppManager<R> {
self.webview.manager()
}
fn manager_owned(&self) -> Arc<AppManager<R>> {
self.webview.manager_owned()
}
fn runtime(&self) -> RuntimeOrDispatch<'_, R> {
self.webview.runtime()
}
fn managed_app_handle(&self) -> &AppHandle<R> {
self.webview.managed_app_handle()
}
}
impl<'de, R: Runtime> CommandArg<'de, R> for WebviewWindow<R> {
/// Grabs the [`Window`] from the [`CommandItem`]. This will never fail.
fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError> {
@ -1702,7 +1684,7 @@ tauri::Builder::default()
/// Event system APIs.
impl<R: Runtime> WebviewWindow<R> {
/// Listen to an event on this webview.
/// Listen to an event on this webview window.
///
/// # Examples
///
@ -1728,10 +1710,16 @@ tauri::Builder::default()
where
F: Fn(Event) + Send + 'static,
{
self.webview.listen(event, handler)
self.manager().listen(
event.into(),
EventTarget::WebviewWindow {
label: self.label().to_string(),
},
handler,
)
}
/// Unlisten to an event on this window.
/// Unlisten to an event on this webview window.
///
/// # Examples
#[cfg_attr(
@ -1761,39 +1749,42 @@ tauri::Builder::default()
"####
)]
pub fn unlisten(&self, id: EventId) {
self.webview.unlisten(id)
self.manager().unlisten(id)
}
/// Listen to an event on this webview only once.
/// Listen to an event on this window webview only once.
///
/// See [`Self::listen`] for more information.
pub fn once<F>(&self, event: impl Into<String>, handler: F)
where
F: FnOnce(Event) + Send + 'static,
{
self.webview.once(event, handler)
self.manager().once(
event.into(),
EventTarget::WebviewWindow {
label: self.label().to_string(),
},
handler,
)
}
}
impl<R: Runtime> Manager<R> for WebviewWindow<R> {
fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
self.webview.emit(event, payload)
impl<R: Runtime> Manager<R> for WebviewWindow<R> {}
impl<R: Runtime> ManagerBase<R> for WebviewWindow<R> {
fn manager(&self) -> &AppManager<R> {
self.webview.manager()
}
fn emit_to<S: Serialize + Clone>(
&self,
label: &str,
event: &str,
payload: S,
) -> crate::Result<()> {
self.webview.emit_to(label, event, payload)
fn manager_owned(&self) -> Arc<AppManager<R>> {
self.webview.manager_owned()
}
fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> crate::Result<()>
where
S: Serialize + Clone,
F: Fn(&Webview<R>) -> bool,
{
self.webview.emit_filter(event, payload, filter)
fn runtime(&self) -> RuntimeOrDispatch<'_, R> {
self.webview.runtime()
}
fn managed_app_handle(&self) -> &AppHandle<R> {
self.webview.managed_app_handle()
}
}

View File

@ -14,7 +14,7 @@ pub use tauri_utils::{config::Color, WindowEffect as Effect, WindowEffectState a
use crate::{
app::AppHandle,
event::{Event, EventId, EventSource},
event::{Event, EventId, EventTarget},
ipc::{CommandArg, CommandItem, InvokeError},
manager::{webview::WebviewLabelDef, AppManager},
runtime::{
@ -911,60 +911,7 @@ impl<R: Runtime> PartialEq for Window<R> {
}
}
impl<R: Runtime> Manager<R> for Window<R> {
#[cfg_attr(
feature = "tracing",
tracing::instrument("window::emit", skip(self, payload))
)]
fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
// store the webviews before emit_filter() to prevent a deadlock
let webviews = self.webviews();
self.manager().emit_filter(
event,
EventSource::Window {
label: self.label().to_string(),
},
payload,
|w| webviews.contains(w),
)?;
Ok(())
}
fn emit_to<S: Serialize + Clone>(
&self,
label: &str,
event: &str,
payload: S,
) -> crate::Result<()> {
self.manager().emit_filter(
event,
EventSource::Window {
label: self.label().to_string(),
},
payload,
|w| label == w.label(),
)
}
#[cfg_attr(
feature = "tracing",
tracing::instrument("window::emit::filter", skip(self, payload, filter))
)]
fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> crate::Result<()>
where
S: Serialize + Clone,
F: Fn(&Webview<R>) -> bool,
{
self.manager().emit_filter(
event,
EventSource::Window {
label: self.label().to_string(),
},
payload,
filter,
)
}
}
impl<R: Runtime> Manager<R> for Window<R> {}
impl<R: Runtime> ManagerBase<R> for Window<R> {
fn manager(&self) -> &AppManager<R> {
@ -1021,7 +968,7 @@ impl<R: Runtime> Window<R> {
}
/// Adds a new webview as a child of this window.
#[cfg(all(desktop, feature = "unstable"))]
#[cfg(any(test, all(desktop, feature = "unstable")))]
#[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "unstable"))))]
pub fn add_child<P: Into<Position>, S: Into<Size>>(
&self,
@ -1993,8 +1940,13 @@ tauri::Builder::default()
where
F: Fn(Event) + Send + 'static,
{
// TODO: listen on all webviews
self.manager.listen(event.into(), None, handler)
self.manager.listen(
event.into(),
EventTarget::Window {
label: self.label().to_string(),
},
handler,
)
}
/// Unlisten to an event on this window.
@ -2037,8 +1989,13 @@ tauri::Builder::default()
where
F: FnOnce(Event) + Send + 'static,
{
// TODO: listen on all webviews
self.manager.once(event.into(), None, handler)
self.manager.once(
event.into(),
EventTarget::Window {
label: self.label().to_string(),
},
handler,
)
}
}

View File

@ -46,7 +46,7 @@ mod tests {
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") {
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 fixture_plugins_str = read_to_string(fixture_entry.path().join("required-plugins.json"))
.expect("failed to read fixture required-plugins.json file");

View File

@ -24,11 +24,10 @@
const container = document.getElementById('container')
function createWindowMessageBtn(label) {
const webview = WebviewWindow.getByLabel(label)
const button = document.createElement('button')
button.innerText = 'Send message to ' + label
button.addEventListener('click', function () {
webview.emit('clicked', 'message from ' + windowLabel)
appWindow.emitTo(label, 'clicked', 'message from ' + windowLabel)
})
container.appendChild(button)
}
@ -67,7 +66,7 @@
globalMessageButton.innerHTML = 'Send global message'
globalMessageButton.addEventListener('click', function () {
// emit to all windows
window.__TAURI__.event.emit('clicked', 'message from ' + windowLabel)
appWindow.emit('clicked', 'message from ' + windowLabel)
})
container.appendChild(globalMessageButton)
@ -82,4 +81,4 @@
</script>
</body>
</html>
</html>

View File

@ -5,8 +5,30 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use tauri::{webview::PageLoadEvent, WebviewWindowBuilder};
use tauri_utils::acl::{
resolved::{CommandKey, ResolvedCommand},
ExecutionContext,
};
fn main() {
let mut context = tauri::generate_context!("../../examples/multiwindow/tauri.conf.json");
for cmd in [
"plugin:event|listen",
"plugin:event|emit",
"plugin:event|emit_to",
] {
context.resolved_acl().allowed_commands.insert(
CommandKey {
name: cmd.into(),
context: ExecutionContext::Local,
},
ResolvedCommand {
windows: vec!["*".parse().unwrap()],
..Default::default()
},
);
}
tauri::Builder::default()
.on_page_load(|webview, payload| {
if payload.event() == PageLoadEvent::Finished {
@ -28,8 +50,6 @@ fn main() {
Ok(())
})
.run(tauri::generate_context!(
"../../examples/multiwindow/tauri.conf.json"
))
.run(context)
.expect("failed to run tauri application");
}

View File

@ -11,24 +11,17 @@
import { invoke, transformCallback } from './core'
type EventSource =
| {
kind: 'global'
}
| {
kind: 'window'
label: string
}
| {
kind: 'webview'
label: string
}
type EventTarget =
| { kind: 'Any' }
| { kind: 'AnyLabel'; label: string }
| { kind: 'App' }
| { kind: 'Window'; label: string }
| { kind: 'Webview'; label: string }
| { kind: 'WebviewWindow'; label: string }
interface Event<T> {
/** Event name */
event: EventName
/** The source of the event. Can be a global event, an event from a window or an event from another webview. */
source: EventSource
/** Event identifier used to unlisten */
id: number
/** Event payload */
@ -43,16 +36,11 @@ type EventName = `${TauriEvent}` | (string & Record<never, never>)
interface Options {
/**
* Window or webview the function targets.
* The event target to listen to, defaults to `{ kind: 'Any' }`, see {@link EventTarget}.
*
* When listening to events and using this value,
* only events triggered by the window with the given label are received.
*
* When emitting events, only the window with the given label will receive it.
* If a string is provided, {@link EventTarget.AnyLabel} is used.
*/
target?:
| { kind: 'window'; label: string }
| { kind: 'webview'; label: string }
target?: string | EventTarget
}
/**
@ -89,14 +77,13 @@ async function _unlisten(event: string, eventId: number): Promise<void> {
}
/**
* Listen to an event. The event can be either global or window-specific.
* See {@link Event.source} to check the event source.
* Listen to an emitted event to any {@link EventTarget|target}.
*
* @example
* ```typescript
* import { listen } from '@tauri-apps/api/event';
* const unlisten = await listen<string>('error', (event) => {
* console.log(`Got error in window ${event.source}, payload: ${event.payload}`);
* console.log(`Got error, payload: ${event.payload}`);
* });
*
* // you need to call unlisten if your handler goes out of scope e.g. the component is unmounted
@ -105,6 +92,7 @@ async function _unlisten(event: string, eventId: number): Promise<void> {
*
* @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`.
* @param handler Event handler callback.
* @param options Event listening options.
* @returns A promise resolving to a function to unlisten to the event.
* Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted.
*
@ -115,9 +103,13 @@ async function listen<T>(
handler: EventCallback<T>,
options?: Options
): Promise<UnlistenFn> {
const target: EventTarget =
typeof options?.target === 'string'
? { kind: 'AnyLabel', label: options.target }
: options?.target ?? { kind: 'Any' }
return invoke<number>('plugin:event|listen', {
event,
target: options?.target,
target,
handler: transformCallback(handler)
}).then((eventId) => {
return async () => _unlisten(event, eventId)
@ -125,7 +117,7 @@ async function listen<T>(
}
/**
* Listen to an one-off event. See {@link listen} for more information.
* Listens once to an emitted event to any {@link EventTarget|target}.
*
* @example
* ```typescript
@ -143,6 +135,8 @@ async function listen<T>(
* ```
*
* @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`.
* @param handler Event handler callback.
* @param options Event listening options.
* @returns A promise resolving to a function to unlisten to the event.
* Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted.
*
@ -164,7 +158,8 @@ async function once<T>(
}
/**
* Emits an event to the backend and all Tauri windows.
* Emits an event to all {@link EventTarget|targets}.
*
* @example
* ```typescript
* import { emit } from '@tauri-apps/api/event';
@ -172,28 +167,53 @@ async function once<T>(
* ```
*
* @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`.
* @param payload Event payload.
*
* @since 1.0.0
*/
async function emit(
event: string,
payload?: unknown,
options?: Options
): Promise<void> {
async function emit(event: string, payload?: unknown): Promise<void> {
await invoke('plugin:event|emit', {
event,
target: options?.target,
payload
})
}
/**
* Emits an event to all {@link EventTarget|targets} matching the given target.
*
* @example
* ```typescript
* import { emit } from '@tauri-apps/api/event';
* await emit('frontend-loaded', { loggedIn: true, token: 'authToken' });
* ```
*
* @param target Label of the target Window/Webview/WebviewWindow or raw {@link EventTarget} object.
* @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`.
* @param payload Event payload.
*
* @since 1.0.0
*/
async function emitTo(
target: EventTarget | string,
event: string,
payload?: unknown
): Promise<void> {
const eventTarget: EventTarget =
typeof target === 'string' ? { kind: 'AnyLabel', label: target } : target
await invoke('plugin:event|emit_to', {
target: eventTarget,
event,
payload
})
}
export type {
EventSource,
Event,
EventTarget,
EventCallback,
UnlistenFn,
EventName,
Options
}
export { listen, once, emit, TauriEvent }
export { listen, once, emit, emitTo, TauriEvent }

View File

@ -19,7 +19,16 @@
import { PhysicalPosition, PhysicalSize } from './dpi'
import type { LogicalPosition, LogicalSize } from './dpi'
import type { EventName, EventCallback, UnlistenFn } from './event'
import { TauriEvent, emit, listen, once } from './event'
import {
TauriEvent,
// imported for documentation purposes
// eslint-disable-next-line
type EventTarget,
emit,
emitTo,
listen,
once
} from './event'
import { invoke } from './core'
import { Window, getCurrent as getCurrentWindow } from './window'
import type { WindowOptions } from './window'
@ -190,7 +199,7 @@ class Webview {
}
/**
* Listen to an event emitted by the backend that is tied to the webview.
* Listen to an emitted event on this webview.
*
* @example
* ```typescript
@ -220,12 +229,12 @@ class Webview {
})
}
return listen(event, handler, {
target: { kind: 'webview', label: this.label }
target: { kind: 'Webview', label: this.label }
})
}
/**
* Listen to an one-off event emitted by the backend that is tied to the webview.
* Listen to an emitted event on this webview only once.
*
* @example
* ```typescript
@ -252,12 +261,13 @@ class Webview {
})
}
return once(event, handler, {
target: { kind: 'webview', label: this.label }
target: { kind: 'Webview', label: this.label }
})
}
/**
* Emits an event to the backend, tied to the webview.
* Emits an event to all {@link EventTarget|targets}.
*
* @example
* ```typescript
* import { getCurrent } from '@tauri-apps/api/webview';
@ -274,15 +284,44 @@ class Webview {
handler({
event,
id: -1,
source: { kind: 'webview', label: this.label },
payload
})
}
return Promise.resolve()
}
return emit(event, payload, {
target: { kind: 'webview', label: this.label }
})
return emit(event, payload)
}
/**
* Emits an event to all {@link EventTarget|targets} matching the given target.
*
* @example
* ```typescript
* import { getCurrent } from '@tauri-apps/api/webview';
* await getCurrent().emit('webview-loaded', { loggedIn: true, token: 'authToken' });
* ```
*
* @param target Label of the target Window/Webview/WebviewWindow or raw {@link EventTarget} object.
* @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`.
* @param payload Event payload.
*/
async emitTo(
target: string,
event: string,
payload?: unknown
): Promise<void> {
if (localTauriEvents.includes(event)) {
// eslint-disable-next-line
for (const handler of this.listeners[event] || []) {
handler({
event,
id: -1,
payload
})
}
return Promise.resolve()
}
return emitTo(target, event, payload)
}
/** @ignore */
@ -604,12 +643,79 @@ class WebviewWindow {
// @ts-expect-error `skip` is not defined in the public API but it is handled by the constructor
return getAll().map((w) => new WebviewWindow(w.label, { skip: true }))
}
/**
* Listen to an emitted event on this webivew window.
*
* @example
* ```typescript
* import { WebviewWindow } from '@tauri-apps/api/webview';
* const unlisten = await WebviewWindow.getCurrent().listen<string>('state-changed', (event) => {
* console.log(`Got error: ${payload}`);
* });
*
* // you need to call unlisten if your handler goes out of scope e.g. the component is unmounted
* unlisten();
* ```
*
* @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`.
* @param handler Event handler.
* @returns A promise resolving to a function to unlisten to the event.
* Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted.
*/
async listen<T>(
event: EventName,
handler: EventCallback<T>
): Promise<UnlistenFn> {
if (this._handleTauriEvent(event, handler)) {
return Promise.resolve(() => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, security/detect-object-injection
const listeners = this.listeners[event]
listeners.splice(listeners.indexOf(handler), 1)
})
}
return listen(event, handler, {
target: { kind: 'WebviewWindow', label: this.label }
})
}
/**
* Listen to an emitted event on this webivew window only once.
*
* @example
* ```typescript
* import { WebviewWindow } from '@tauri-apps/api/webview';
* const unlisten = await WebviewWindow.getCurrent().once<null>('initialized', (event) => {
* console.log(`Webview initialized!`);
* });
*
* // you need to call unlisten if your handler goes out of scope e.g. the component is unmounted
* unlisten();
* ```
*
* @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`.
* @param handler Event handler.
* @returns A promise resolving to a function to unlisten to the event.
* Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted.
*/
async once<T>(event: string, handler: EventCallback<T>): Promise<UnlistenFn> {
if (this._handleTauriEvent(event, handler)) {
return Promise.resolve(() => {
// eslint-disable-next-line security/detect-object-injection
const listeners = this.listeners[event]
listeners.splice(listeners.indexOf(handler), 1)
})
}
return once(event, handler, {
target: { kind: 'WebviewWindow', label: this.label }
})
}
}
// order matters, we use window APIs by default
applyMixins(WebviewWindow, [Webview, Window])
// Order matters, we use window APIs by default
applyMixins(WebviewWindow, [Window, Webview])
/** Extends a base class by other specifed classes */
/** Extends a base class by other specifed classes, wihtout overriding existing properties */
function applyMixins(
baseClass: { prototype: unknown },
extendedClasses: unknown
@ -619,6 +725,12 @@ function applyMixins(
: [extendedClasses]
).forEach((extendedClass: { prototype: unknown }) => {
Object.getOwnPropertyNames(extendedClass.prototype).forEach((name) => {
if (
typeof baseClass.prototype === 'object' &&
baseClass.prototype &&
name in baseClass.prototype
)
return
Object.defineProperty(
baseClass.prototype,
name,

View File

@ -22,14 +22,17 @@ import {
PhysicalPosition,
PhysicalSize
} from './dpi'
import type {
Event,
EventName,
EventCallback,
UnlistenFn,
EventSource
import type { Event, EventName, EventCallback, UnlistenFn } from './event'
import {
TauriEvent,
// imported for documentation purposes
// eslint-disable-next-line
type EventTarget,
emit,
emitTo,
listen,
once
} from './event'
import { TauriEvent, emit, listen, once } from './event'
import { invoke } from './core'
import { WebviewWindow } from './webview'
@ -97,15 +100,12 @@ enum UserAttentionType {
class CloseRequestedEvent {
/** Event name */
event: EventName
/** The source of the event. */
source: EventSource
/** Event identifier used to unlisten */
id: number
private _preventDefault = false
constructor(event: Event<null>) {
this.event = event.event
this.source = event.source
this.id = event.id
}
@ -360,7 +360,7 @@ class Window {
}
/**
* Listen to an event emitted by the backend that is tied to the window.
* Listen to an emitted event on this window.
*
* @example
* ```typescript
@ -390,12 +390,12 @@ class Window {
})
}
return listen(event, handler, {
target: { kind: 'window', label: this.label }
target: { kind: 'Window', label: this.label }
})
}
/**
* Listen to an one-off event emitted by the backend that is tied to the window.
* Listen to an emitted event on this window only once.
*
* @example
* ```typescript
@ -422,12 +422,12 @@ class Window {
})
}
return once(event, handler, {
target: { kind: 'window', label: this.label }
target: { kind: 'Window', label: this.label }
})
}
/**
* Emits an event to the backend, tied to the window.
* Emits an event to all {@link EventTarget|targets}.
* @example
* ```typescript
* import { getCurrent } from '@tauri-apps/api/window';
@ -444,15 +444,43 @@ class Window {
handler({
event,
id: -1,
source: { kind: 'window', label: this.label },
payload
})
}
return Promise.resolve()
}
return emit(event, payload, {
target: { kind: 'window', label: this.label }
})
return emit(event, payload)
}
/**
* Emits an event to all {@link EventTarget|targets} matching the given target.
*
* @example
* ```typescript
* import { getCurrent } from '@tauri-apps/api/window';
* await getCurrent().emit('window-loaded', { loggedIn: true, token: 'authToken' });
* ```
* @param target Label of the target Window/Webview/WebviewWindow or raw {@link EventTarget} object.
* @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`.
* @param payload Event payload.
*/
async emitTo(
target: string | EventTarget,
event: string,
payload?: unknown
): Promise<void> {
if (localTauriEvents.includes(event)) {
// eslint-disable-next-line
for (const handler of this.listeners[event] || []) {
handler({
event,
id: -1,
payload
})
}
return Promise.resolve()
}
return emitTo(target, event, payload)
}
/** @ignore */