mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-09-22 21:30:44 +02:00
refactor(shell)!: remove deprecated open API (#3604)
* refactor(shell)!: remove deprecated open API `open` has been deprecated since 2.1.0 in favor of tauri-plugin-opener. This removes: - the `open` command and its `allow-open`/`deny-open` permissions (`shell:default` now grants nothing) - the `plugins > shell > open` configuration; `init()` returns `TauriPlugin<R>` again - `Shell::open`, the `tauri_plugin_shell::open` module and `Error::UnknownProgramName` - the `open` JavaScript function - the Android and iOS plugins, since `open` was their only command * chore(examples): drop the now empty shell:default permission set --------- Co-authored-by: Lucas Nogueira <lucas@crabnebula.dev>
This commit is contained in:
co-authored by
Lucas Nogueira
parent
6a12f7c80a
commit
152ff5bb6d
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"shell": major
|
||||||
|
"shell-js": major
|
||||||
|
---
|
||||||
|
|
||||||
|
**Breaking:** Removed the `open` API, which had been deprecated since v2.1.0 in favor of `tauri-plugin-opener`. This removes the `open` command and its `allow-open`/`deny-open` permissions, the `plugins > shell > open` configuration (`tauri_plugin_shell::init()` now returns `TauriPlugin<R>`), the `Shell::open` method and the `tauri_plugin_shell::open` module, the `Error::UnknownProgramName` variant, and the `open` JavaScript function. The `shell:default` permission set now grants nothing. Since `open` was the only mobile functionality, the Android and iOS plugins were removed as well.
|
||||||
Generated
-1
@@ -8020,7 +8020,6 @@ version = "3.0.0-alpha.1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"log",
|
"log",
|
||||||
"open",
|
|
||||||
"os_pipe",
|
"os_pipe",
|
||||||
"regex",
|
"regex",
|
||||||
"schemars 1.2.2",
|
"schemars 1.2.2",
|
||||||
|
|||||||
@@ -48,7 +48,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"shell:default",
|
|
||||||
"shell:allow-kill",
|
"shell:allow-kill",
|
||||||
"shell:allow-stdin-write",
|
"shell:allow-stdin-write",
|
||||||
"process:allow-exit",
|
"process:allow-exit",
|
||||||
|
|||||||
@@ -72,9 +72,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"shell": {
|
|
||||||
"open": true
|
|
||||||
},
|
|
||||||
"updater": {
|
"updater": {
|
||||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDE5QzMxNjYwNTM5OEUwNTgKUldSWTRKaFRZQmJER1h4d1ZMYVA3dnluSjdpN2RmMldJR09hUFFlZDY0SlFqckkvRUJhZDJVZXAK",
|
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDE5QzMxNjYwNTM5OEUwNTgKUldSWTRKaFRZQmJER1h4d1ZMYVA3dnluSjdpN2RmMldJR09hUFFlZDY0SlFqckkvRUJhZDJVZXAK",
|
||||||
"dangerousInsecureTransportProtocol": true,
|
"dangerousInsecureTransportProtocol": true,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tauri-plugin-shell"
|
name = "tauri-plugin-shell"
|
||||||
version = "3.0.0-alpha.1"
|
version = "3.0.0-alpha.1"
|
||||||
description = "Access the system shell. Allows you to spawn child processes and manage files and URLs using their default application."
|
description = "Access the system shell. Allows you to spawn child processes."
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
authors = { workspace = true }
|
authors = { workspace = true }
|
||||||
license = { workspace = true }
|
license = { workspace = true }
|
||||||
@@ -21,8 +21,8 @@ exclude = [
|
|||||||
windows = { level = "full", notes = "" }
|
windows = { level = "full", notes = "" }
|
||||||
linux = { level = "full", notes = "" }
|
linux = { level = "full", notes = "" }
|
||||||
macos = { level = "full", notes = "" }
|
macos = { level = "full", notes = "" }
|
||||||
android = { level = "partial", notes = "Only allows to open URLs via `open`" }
|
android = { level = "none", notes = "" }
|
||||||
ios = { level = "partial", notes = "Only allows to open URLs via `open`" }
|
ios = { level = "none", notes = "" }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-plugin = { workspace = true, features = ["build"] }
|
tauri-plugin = { workspace = true, features = ["build"] }
|
||||||
@@ -38,6 +38,5 @@ log = { workspace = true }
|
|||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
shared_child = "1"
|
shared_child = "1"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
open = { version = "5", features = ["shellexecute-on-windows"] }
|
|
||||||
encoding_rs = "0.8"
|
encoding_rs = "0.8"
|
||||||
os_pipe = "1"
|
os_pipe = "1"
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||

|

|
||||||
|
|
||||||
Access the system shell. Allows you to spawn child processes and manage files and URLs using their default application.
|
Access the system shell. Allows you to spawn child processes.
|
||||||
|
|
||||||
|
To open files and URLs with their default application, use [tauri-plugin-opener](../opener).
|
||||||
|
|
||||||
| Platform | Supported |
|
| Platform | Supported |
|
||||||
| -------- | --------- |
|
| -------- | --------- |
|
||||||
| Linux | ✓ |
|
| Linux | ✓ |
|
||||||
| Windows | ✓ |
|
| Windows | ✓ |
|
||||||
| macOS | ✓ |
|
| macOS | ✓ |
|
||||||
| Android | ✓ |
|
| Android | x |
|
||||||
| iOS | ✓ |
|
| iOS | x |
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
/build
|
|
||||||
/.tauri
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id("com.android.library")
|
|
||||||
id("org.jetbrains.kotlin.android")
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace = "app.tauri.shell"
|
|
||||||
compileSdk = 36
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
minSdk = 24
|
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
|
||||||
consumerProguardFiles("consumer-rules.pro")
|
|
||||||
}
|
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
|
||||||
}
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "1.8"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation("androidx.core:core-ktx:1.9.0")
|
|
||||||
implementation("com.fasterxml.jackson.core:jackson-databind:2.15.3")
|
|
||||||
implementation(project(":tauri-android"))
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
# Add project specific ProGuard rules here.
|
|
||||||
# You can control the set of applied configuration files using the
|
|
||||||
# proguardFiles setting in build.gradle.
|
|
||||||
#
|
|
||||||
# For more details, see
|
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
||||||
|
|
||||||
# If your project uses WebView with JS, uncomment the following
|
|
||||||
# and specify the fully qualified class name to the JavaScript interface
|
|
||||||
# class:
|
|
||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
||||||
# public *;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# Uncomment this to preserve the line number information for
|
|
||||||
# debugging stack traces.
|
|
||||||
#-keepattributes SourceFile,LineNumberTable
|
|
||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
|
||||||
# hide the original source file name.
|
|
||||||
#-renamesourcefileattribute SourceFile
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
include ':tauri-android'
|
|
||||||
project(':tauri-android').projectDir = new File('./.tauri/tauri-api')
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
</manifest>
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package app.tauri.shell
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
|
||||||
import app.tauri.annotation.Command
|
|
||||||
import app.tauri.annotation.TauriPlugin
|
|
||||||
import app.tauri.plugin.Invoke
|
|
||||||
import app.tauri.plugin.Plugin
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
@TauriPlugin
|
|
||||||
class ShellPlugin(private val activity: Activity) : Plugin(activity) {
|
|
||||||
@Command
|
|
||||||
fun open(invoke: Invoke) {
|
|
||||||
try {
|
|
||||||
val url = invoke.parseArgs(String::class.java)
|
|
||||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
activity.applicationContext?.startActivity(intent)
|
|
||||||
invoke.resolve()
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
invoke.reject(ex.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +1 @@
|
|||||||
if("__TAURI__"in window){var __TAURI_PLUGIN_SHELL__=function(e){"use strict";function t(e,t,s,i){if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===s?i:"a"===s?i.call(e):i?i.value:t.get(e)}function s(e,t,s,i,n){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return t.set(e,s),s}var i,n,r,o;"function"==typeof SuppressedError&&SuppressedError;const a="__TAURI_TO_IPC_KEY__";class h{constructor(e){i.set(this,void 0),n.set(this,0),r.set(this,[]),o.set(this,void 0),s(this,i,e||(()=>{})),this.id=function(e,t=!1){return window.__TAURI_INTERNALS__.transformCallback(e,t)}(e=>{const a=e.index;if("end"in e)return void(a==t(this,n,"f")?this.cleanupCallback():s(this,o,a));const h=e.message;if(a==t(this,n,"f")){for(t(this,i,"f").call(this,h),s(this,n,t(this,n,"f")+1);t(this,n,"f")in t(this,r,"f");){const e=t(this,r,"f")[t(this,n,"f")];t(this,i,"f").call(this,e),delete t(this,r,"f")[t(this,n,"f")],s(this,n,t(this,n,"f")+1)}t(this,n,"f")===t(this,o,"f")&&this.cleanupCallback()}else t(this,r,"f")[a]=h})}cleanupCallback(){window.__TAURI_INTERNALS__.unregisterCallback(this.id)}set onmessage(e){s(this,i,e)}get onmessage(){return t(this,i,"f")}[(i=new WeakMap,n=new WeakMap,r=new WeakMap,o=new WeakMap,a)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[a]()}}async function c(e,t={},s){return window.__TAURI_INTERNALS__.invoke(e,t,s)}class l{constructor(){this.eventListeners=Object.create(null)}addListener(e,t){return this.on(e,t)}removeListener(e,t){return this.off(e,t)}on(e,t){return e in this.eventListeners?this.eventListeners[e].push(t):this.eventListeners[e]=[t],this}once(e,t){const s=i=>{this.removeListener(e,s),t(i)};return this.addListener(e,s)}off(e,t){return e in this.eventListeners&&(this.eventListeners[e]=this.eventListeners[e].filter(e=>e!==t)),this}removeAllListeners(e){return e?delete this.eventListeners[e]:this.eventListeners=Object.create(null),this}emit(e,t){if(e in this.eventListeners){const s=this.eventListeners[e];for(const e of s)e(t);return!0}return!1}listenerCount(e){return e in this.eventListeners?this.eventListeners[e].length:0}prependListener(e,t){return e in this.eventListeners?this.eventListeners[e].unshift(t):this.eventListeners[e]=[t],this}prependOnceListener(e,t){const s=i=>{this.removeListener(e,s),t(i)};return this.prependListener(e,s)}}class u{constructor(e){this.pid=e}async write(e){await c("plugin:shell|stdin_write",{pid:this.pid,buffer:e})}async kill(){await c("plugin:shell|kill",{cmd:"killChild",pid:this.pid})}}class p extends l{constructor(e,t=[],s){super(),this.stdout=new l,this.stderr=new l,this.program=e,this.args="string"==typeof t?[t]:t,this.options=s??{}}static create(e,t=[],s){return new p(e,t,s)}static sidecar(e,t=[],s){const i=new p(e,t,s);return i.options.sidecar=!0,i}async spawn(){const e=this.program,t=this.args,s=this.options;"object"==typeof t&&Object.freeze(t);const i=new h;return i.onmessage=e=>{switch(e.event){case"Error":this.emit("error",e.payload);break;case"Terminated":this.emit("close",e.payload);break;case"Stdout":this.stdout.emit("data",e.payload);break;case"Stderr":this.stderr.emit("data",e.payload)}},await c("plugin:shell|spawn",{program:e,args:t,options:s,onEvent:i}).then(e=>new u(e))}async execute(){const e=this.program,t=this.args,s=this.options;return"object"==typeof t&&Object.freeze(t),await c("plugin:shell|execute",{program:e,args:t,options:s})}}return e.Child=u,e.Command=p,e.EventEmitter=l,e.open=async function(e,t){await c("plugin:shell|open",{path:e,with:t})},e}({});Object.defineProperty(window.__TAURI__,"shell",{value:__TAURI_PLUGIN_SHELL__})}
|
if("__TAURI__"in window){var __TAURI_PLUGIN_SHELL__=function(e){"use strict";function t(e,t,s,i){if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===s?i:"a"===s?i.call(e):i?i.value:t.get(e)}function s(e,t,s,i,n){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return t.set(e,s),s}var i,n,r,o;"function"==typeof SuppressedError&&SuppressedError;const a="__TAURI_TO_IPC_KEY__";class h{constructor(e){i.set(this,void 0),n.set(this,0),r.set(this,[]),o.set(this,void 0),s(this,i,e||(()=>{})),this.id=function(e,t=!1){return window.__TAURI_INTERNALS__.transformCallback(e,t)}(e=>{const a=e.index;if("end"in e)return void(a==t(this,n,"f")?this.cleanupCallback():s(this,o,a));const h=e.message;if(a==t(this,n,"f")){for(t(this,i,"f").call(this,h),s(this,n,t(this,n,"f")+1);t(this,n,"f")in t(this,r,"f");){const e=t(this,r,"f")[t(this,n,"f")];t(this,i,"f").call(this,e),delete t(this,r,"f")[t(this,n,"f")],s(this,n,t(this,n,"f")+1)}t(this,n,"f")===t(this,o,"f")&&this.cleanupCallback()}else t(this,r,"f")[a]=h})}cleanupCallback(){window.__TAURI_INTERNALS__.unregisterCallback(this.id)}set onmessage(e){s(this,i,e)}get onmessage(){return t(this,i,"f")}[(i=new WeakMap,n=new WeakMap,r=new WeakMap,o=new WeakMap,a)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[a]()}}async function c(e,t={},s){return window.__TAURI_INTERNALS__.invoke(e,t,s)}class l{constructor(){this.eventListeners=Object.create(null)}addListener(e,t){return this.on(e,t)}removeListener(e,t){return this.off(e,t)}on(e,t){return e in this.eventListeners?this.eventListeners[e].push(t):this.eventListeners[e]=[t],this}once(e,t){const s=i=>{this.removeListener(e,s),t(i)};return this.addListener(e,s)}off(e,t){return e in this.eventListeners&&(this.eventListeners[e]=this.eventListeners[e].filter(e=>e!==t)),this}removeAllListeners(e){return e?delete this.eventListeners[e]:this.eventListeners=Object.create(null),this}emit(e,t){if(e in this.eventListeners){const s=this.eventListeners[e];for(const e of s)e(t);return!0}return!1}listenerCount(e){return e in this.eventListeners?this.eventListeners[e].length:0}prependListener(e,t){return e in this.eventListeners?this.eventListeners[e].unshift(t):this.eventListeners[e]=[t],this}prependOnceListener(e,t){const s=i=>{this.removeListener(e,s),t(i)};return this.prependListener(e,s)}}class u{constructor(e){this.pid=e}async write(e){await c("plugin:shell|stdin_write",{pid:this.pid,buffer:e})}async kill(){await c("plugin:shell|kill",{cmd:"killChild",pid:this.pid})}}class d extends l{constructor(e,t=[],s){super(),this.stdout=new l,this.stderr=new l,this.program=e,this.args="string"==typeof t?[t]:t,this.options=s??{}}static create(e,t=[],s){return new d(e,t,s)}static sidecar(e,t=[],s){const i=new d(e,t,s);return i.options.sidecar=!0,i}async spawn(){const e=this.program,t=this.args,s=this.options;"object"==typeof t&&Object.freeze(t);const i=new h;return i.onmessage=e=>{switch(e.event){case"Error":this.emit("error",e.payload);break;case"Terminated":this.emit("close",e.payload);break;case"Stdout":this.stdout.emit("data",e.payload);break;case"Stderr":this.stderr.emit("data",e.payload)}},await c("plugin:shell|spawn",{program:e,args:t,options:s,onEvent:i}).then(e=>new u(e))}async execute(){const e=this.program,t=this.args,s=this.options;return"object"==typeof t&&Object.freeze(t),await c("plugin:shell|execute",{program:e,args:t,options:s})}}return e.Child=u,e.Command=d,e.EventEmitter=l,e}({});Object.defineProperty(window.__TAURI__,"shell",{value:__TAURI_PLUGIN_SHELL__})}
|
||||||
|
|||||||
+1
-17
@@ -163,7 +163,7 @@ fn _f() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const COMMANDS: &[&str] = &["execute", "spawn", "stdin_write", "kill", "open"];
|
const COMMANDS: &[&str] = &["execute", "spawn", "stdin_write", "kill"];
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
tauri_plugin::Builder::new(COMMANDS)
|
tauri_plugin::Builder::new(COMMANDS)
|
||||||
@@ -172,21 +172,5 @@ fn main() {
|
|||||||
schemars::SchemaGenerator::new(schemars::generate::SchemaSettings::draft07())
|
schemars::SchemaGenerator::new(schemars::generate::SchemaSettings::draft07())
|
||||||
.into_root_schema_for::<ShellScopeEntry>(),
|
.into_root_schema_for::<ShellScopeEntry>(),
|
||||||
)
|
)
|
||||||
.android_path("android")
|
|
||||||
.ios_path("ios")
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
|
|
||||||
let mobile = target_os == "ios" || target_os == "android";
|
|
||||||
alias("desktop", !mobile);
|
|
||||||
alias("mobile", mobile);
|
|
||||||
}
|
|
||||||
|
|
||||||
// creates a cfg alias if `has_feature` is true.
|
|
||||||
// `alias` must be a snake case string.
|
|
||||||
fn alias(alias: &str, has_feature: bool) {
|
|
||||||
println!("cargo:rustc-check-cfg=cfg({alias})");
|
|
||||||
if has_feature {
|
|
||||||
println!("cargo:rustc-cfg={alias}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,18 +4,14 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Access the system shell.
|
* Access the system shell.
|
||||||
* Allows you to spawn child processes and manage files and URLs using their default application.
|
* Allows you to spawn child processes.
|
||||||
|
*
|
||||||
|
* To open files and URLs with their default application, use `@tauri-apps/plugin-opener`.
|
||||||
*
|
*
|
||||||
* ## Security
|
* ## Security
|
||||||
*
|
*
|
||||||
* This API has a scope configuration that forces you to restrict the programs and arguments that can be used.
|
* This API has a scope configuration that forces you to restrict the programs and arguments that can be used.
|
||||||
*
|
*
|
||||||
* ### Restricting access to the {@link open | `open`} API
|
|
||||||
*
|
|
||||||
* On the configuration object, `open: true` means that the {@link open} API can be used with any URL,
|
|
||||||
* as the argument is validated with the `^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+` regex.
|
|
||||||
* You can change that regex by changing the boolean value to a string, e.g. `open: ^https://github.com/`.
|
|
||||||
*
|
|
||||||
* ### Restricting access to the {@link Command | `Command`} APIs
|
* ### Restricting access to the {@link Command | `Command`} APIs
|
||||||
*
|
*
|
||||||
* The plugin permissions object has a `scope` field that defines an array of CLIs that can be used.
|
* The plugin permissions object has a `scope` field that defines an array of CLIs that can be used.
|
||||||
@@ -572,40 +568,7 @@ type CommandEvent<O extends IOPayload> =
|
|||||||
| Event<'Terminated', TerminatedPayload>
|
| Event<'Terminated', TerminatedPayload>
|
||||||
| Event<'Error', string>
|
| Event<'Error', string>
|
||||||
|
|
||||||
/**
|
export { Command, Child, EventEmitter }
|
||||||
* Opens a path or URL with the system's default app,
|
|
||||||
* or the one specified with `openWith`.
|
|
||||||
*
|
|
||||||
* The `openWith` value must be one of `firefox`, `google chrome`, `chromium` `safari`,
|
|
||||||
* `open`, `start`, `xdg-open`, `gio`, `gnome-open`, `kde-open` or `wslview`.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```typescript
|
|
||||||
* import { open } from '@tauri-apps/plugin-shell';
|
|
||||||
* // opens the given URL on the default browser:
|
|
||||||
* await open('https://github.com/tauri-apps/tauri');
|
|
||||||
* // opens the given URL using `firefox`:
|
|
||||||
* await open('https://github.com/tauri-apps/tauri', 'firefox');
|
|
||||||
* // opens a file using the default program:
|
|
||||||
* await open('/path/to/file');
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @param path The path or URL to open.
|
|
||||||
* This value is matched against the string regex defined on `tauri.conf.json > plugins > shell > open`,
|
|
||||||
* which defaults to `^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+`.
|
|
||||||
* @param openWith The app to open the file or URL with.
|
|
||||||
* Defaults to the system default application for the specified path type.
|
|
||||||
*
|
|
||||||
* @since 2.0.0
|
|
||||||
*/
|
|
||||||
async function open(path: string, openWith?: string): Promise<void> {
|
|
||||||
await invoke('plugin:shell|open', {
|
|
||||||
path,
|
|
||||||
with: openWith
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export { Command, Child, EventEmitter, open }
|
|
||||||
export type {
|
export type {
|
||||||
IOPayload,
|
IOPayload,
|
||||||
CommandEvents,
|
CommandEvents,
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"object": {
|
|
||||||
"pins": [
|
|
||||||
{
|
|
||||||
"package": "SwiftRs",
|
|
||||||
"repositoryURL": "https://github.com/Brendonovich/swift-rs",
|
|
||||||
"state": {
|
|
||||||
"branch": null,
|
|
||||||
"revision": "b5ed223fcdab165bc21219c1925dc1e77e2bef5e",
|
|
||||||
"version": "1.0.6"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"version": 1
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
// swift-tools-version:5.3
|
|
||||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
import PackageDescription
|
|
||||||
|
|
||||||
let package = Package(
|
|
||||||
name: "tauri-plugin-shell",
|
|
||||||
platforms: [
|
|
||||||
.macOS(.v10_13),
|
|
||||||
.iOS(.v13),
|
|
||||||
],
|
|
||||||
products: [
|
|
||||||
// Products define the executables and libraries a package produces, and make them visible to other packages.
|
|
||||||
.library(
|
|
||||||
name: "tauri-plugin-shell",
|
|
||||||
type: .static,
|
|
||||||
targets: ["tauri-plugin-shell"])
|
|
||||||
],
|
|
||||||
dependencies: [
|
|
||||||
.package(name: "Tauri", path: "../.tauri/tauri-api")
|
|
||||||
],
|
|
||||||
targets: [
|
|
||||||
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
|
||||||
// Targets can depend on other targets in this package, and on products in packages this package depends on.
|
|
||||||
.target(
|
|
||||||
name: "tauri-plugin-shell",
|
|
||||||
dependencies: [
|
|
||||||
.byName(name: "Tauri")
|
|
||||||
],
|
|
||||||
path: "Sources")
|
|
||||||
]
|
|
||||||
)
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
import SwiftRs
|
|
||||||
import Tauri
|
|
||||||
import UIKit
|
|
||||||
import WebKit
|
|
||||||
|
|
||||||
class ShellPlugin: Plugin {
|
|
||||||
|
|
||||||
@objc public func open(_ invoke: Invoke) throws {
|
|
||||||
do {
|
|
||||||
let urlString = try invoke.parseArgs(String.self)
|
|
||||||
if let url = URL(string: urlString) {
|
|
||||||
if #available(iOS 10, *) {
|
|
||||||
UIApplication.shared.open(url, options: [:])
|
|
||||||
} else {
|
|
||||||
UIApplication.shared.openURL(url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
invoke.resolve()
|
|
||||||
} catch {
|
|
||||||
invoke.reject(error.localizedDescription)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@_cdecl("init_plugin_shell")
|
|
||||||
func initPlugin() -> Plugin {
|
|
||||||
return ShellPlugin()
|
|
||||||
}
|
|
||||||
@@ -5,13 +5,8 @@ shell functionality is exposed by default.
|
|||||||
|
|
||||||
#### Granted Permissions
|
#### Granted Permissions
|
||||||
|
|
||||||
It allows to use the `open` functionality with a reasonable
|
Nothing is granted by default. Spawning processes must be explicitly
|
||||||
scope pre-configured. It will allow opening `http(s)://`,
|
allowed with a scope, see `allow-execute` and `allow-spawn`.
|
||||||
`tel:` and `mailto:` links.
|
|
||||||
|
|
||||||
#### This default permission set includes the following:
|
|
||||||
|
|
||||||
- `allow-open`
|
|
||||||
|
|
||||||
## Permission Table
|
## Permission Table
|
||||||
|
|
||||||
@@ -77,32 +72,6 @@ Denies the kill command without any pre-configured scope.
|
|||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
`shell:allow-open`
|
|
||||||
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
Enables the open command without any pre-configured scope.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
`shell:deny-open`
|
|
||||||
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
Denies the open command without any pre-configured scope.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
`shell:allow-spawn`
|
`shell:allow-spawn`
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -7,9 +7,8 @@ shell functionality is exposed by default.
|
|||||||
|
|
||||||
#### Granted Permissions
|
#### Granted Permissions
|
||||||
|
|
||||||
It allows to use the `open` functionality with a reasonable
|
Nothing is granted by default. Spawning processes must be explicitly
|
||||||
scope pre-configured. It will allow opening `http(s)://`,
|
allowed with a scope, see `allow-execute` and `allow-spawn`.
|
||||||
`tel:` and `mailto:` links.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
permissions = ["allow-open"]
|
permissions = []
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ use tauri::{
|
|||||||
Manager, Runtime, State, Window,
|
Manager, Runtime, State, Window,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
use crate::open::Program;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
process::{CommandEvent, TerminatedPayload},
|
process::{CommandEvent, TerminatedPayload},
|
||||||
scope::ExecuteArgs,
|
scope::ExecuteArgs,
|
||||||
@@ -307,14 +305,3 @@ pub fn kill<R: Runtime>(
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn open<R: Runtime>(
|
|
||||||
_window: Window<R>,
|
|
||||||
shell: State<'_, Shell<R>>,
|
|
||||||
path: String,
|
|
||||||
with: Option<Program>,
|
|
||||||
) -> crate::Result<()> {
|
|
||||||
crate::open::open(Some(&shell.open_scope), path, with)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
/// Configuration for the shell plugin.
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
|
||||||
pub struct Config {
|
|
||||||
/// Open URL with the user's default application.
|
|
||||||
#[serde(default)]
|
|
||||||
pub open: ShellAllowlistOpen,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defines the `shell > open` api scope.
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Deserialize)]
|
|
||||||
#[serde(untagged, deny_unknown_fields)]
|
|
||||||
#[non_exhaustive]
|
|
||||||
#[derive(Default)]
|
|
||||||
pub enum ShellAllowlistOpen {
|
|
||||||
/// Shell open API allowlist is not defined by the user.
|
|
||||||
/// In this case we add the default validation regex (same as [`Self::Flag(true)`]).
|
|
||||||
#[default]
|
|
||||||
Unset,
|
|
||||||
/// If the shell open API should be enabled.
|
|
||||||
///
|
|
||||||
/// If enabled, the default validation regex (`^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+`) is used.
|
|
||||||
Flag(bool),
|
|
||||||
|
|
||||||
/// Enable the shell open API, with a custom regex that the opened path must match against.
|
|
||||||
///
|
|
||||||
/// The regex string is automatically surrounded by `^...$` to match the full string.
|
|
||||||
/// For example the `https?://\w+` regex would be registered as `^https?://\w+$`.
|
|
||||||
///
|
|
||||||
/// If using a custom regex to support a non-http(s) schema, care should be used to prevent values
|
|
||||||
/// that allow flag-like strings to pass validation. e.g. `--enable-debugging`, `-i`, `/R`.
|
|
||||||
Validate(String),
|
|
||||||
}
|
|
||||||
@@ -8,15 +8,10 @@ use serde::{Serialize, Serializer};
|
|||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[cfg(mobile)]
|
|
||||||
#[error(transparent)]
|
|
||||||
PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError),
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
#[error("current executable path has no parent")]
|
#[error("current executable path has no parent")]
|
||||||
CurrentExeHasNoParent,
|
CurrentExeHasNoParent,
|
||||||
#[error("unknown program {0}")]
|
|
||||||
UnknownProgramName(String),
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Scope(#[from] crate::scope::Error),
|
Scope(#[from] crate::scope::Error),
|
||||||
/// Sidecar not allowed by the configuration.
|
/// Sidecar not allowed by the configuration.
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
//! Access the system shell. Allows you to spawn child processes and manage files and URLs using their default application.
|
//! Access the system shell. Allows you to spawn child processes.
|
||||||
|
//!
|
||||||
|
//! To open files and URLs with their default application, use `tauri-plugin-opener`.
|
||||||
|
|
||||||
#![doc(
|
#![doc(
|
||||||
html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png",
|
html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png",
|
||||||
@@ -17,18 +19,13 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use process::{Command, CommandChild};
|
use process::{Command, CommandChild};
|
||||||
use regex::Regex;
|
|
||||||
use tauri::{
|
use tauri::{
|
||||||
plugin::{Builder, TauriPlugin},
|
plugin::{Builder, TauriPlugin},
|
||||||
AppHandle, Manager, RunEvent, Runtime,
|
AppHandle, Manager, RunEvent, Runtime,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod commands;
|
mod commands;
|
||||||
mod config;
|
|
||||||
mod error;
|
mod error;
|
||||||
#[deprecated(since = "2.1.0", note = "Use tauri-plugin-opener instead.")]
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub mod open;
|
|
||||||
pub mod process;
|
pub mod process;
|
||||||
mod scope;
|
mod scope;
|
||||||
mod scope_entry;
|
mod scope_entry;
|
||||||
@@ -36,21 +33,11 @@ mod scope_entry;
|
|||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
type Result<T> = std::result::Result<T, Error>;
|
type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
#[cfg(mobile)]
|
|
||||||
use tauri::plugin::PluginHandle;
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
const PLUGIN_IDENTIFIER: &str = "app.tauri.shell";
|
|
||||||
#[cfg(target_os = "ios")]
|
|
||||||
tauri::ios_plugin_binding!(init_plugin_shell);
|
|
||||||
|
|
||||||
type ChildStore = Arc<Mutex<HashMap<u32, CommandChild>>>;
|
type ChildStore = Arc<Mutex<HashMap<u32, CommandChild>>>;
|
||||||
|
|
||||||
pub struct Shell<R: Runtime> {
|
pub struct Shell<R: Runtime> {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
app: AppHandle<R>,
|
app: AppHandle<R>,
|
||||||
#[cfg(mobile)]
|
|
||||||
mobile_plugin_handle: PluginHandle<R>,
|
|
||||||
open_scope: scope::OpenScope,
|
|
||||||
children: ChildStore,
|
children: ChildStore,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,27 +54,6 @@ impl<R: Runtime> Shell<R> {
|
|||||||
pub fn sidecar(&self, program: impl AsRef<Path>) -> Result<Command> {
|
pub fn sidecar(&self, program: impl AsRef<Path>) -> Result<Command> {
|
||||||
Command::new_sidecar(program)
|
Command::new_sidecar(program)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open a (url) path with a default or specific browser opening program.
|
|
||||||
///
|
|
||||||
/// See [`crate::open::open`] for how it handles security-related measures.
|
|
||||||
#[cfg(desktop)]
|
|
||||||
#[deprecated(since = "2.1.0", note = "Use tauri-plugin-opener instead.")]
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn open(&self, path: impl Into<String>, with: Option<open::Program>) -> Result<()> {
|
|
||||||
open::open(None, path.into(), with)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Open a (url) path with a default or specific browser opening program.
|
|
||||||
///
|
|
||||||
/// See [`crate::open::open`] for how it handles security-related measures.
|
|
||||||
#[cfg(mobile)]
|
|
||||||
#[deprecated(since = "2.1.0", note = "Use tauri-plugin-opener instead.")]
|
|
||||||
pub fn open(&self, path: impl Into<String>, _with: Option<open::Program>) -> Result<()> {
|
|
||||||
self.mobile_plugin_handle
|
|
||||||
.run_mobile_plugin("open", path.into())
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ShellExt<R: Runtime> {
|
pub trait ShellExt<R: Runtime> {
|
||||||
@@ -100,32 +66,19 @@ impl<R: Runtime, T: Manager<R>> ShellExt<R> for T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init<R: Runtime>() -> TauriPlugin<R, Option<config::Config>> {
|
pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||||
Builder::<R, Option<config::Config>>::new("shell")
|
Builder::new("shell")
|
||||||
.initialization_script(include_str!("init-iife.js").to_string())
|
.initialization_script(include_str!("init-iife.js").to_string())
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
commands::execute,
|
commands::execute,
|
||||||
commands::spawn,
|
commands::spawn,
|
||||||
commands::stdin_write,
|
commands::stdin_write,
|
||||||
commands::kill,
|
commands::kill,
|
||||||
commands::open
|
|
||||||
])
|
])
|
||||||
.setup(|app, api| {
|
.setup(|app, _api| {
|
||||||
let default_config = config::Config::default();
|
|
||||||
let config = api.config().as_ref().unwrap_or(&default_config);
|
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
let handle = api.register_android_plugin(PLUGIN_IDENTIFIER, "ShellPlugin")?;
|
|
||||||
#[cfg(target_os = "ios")]
|
|
||||||
let handle = api.register_ios_plugin(init_plugin_shell)?;
|
|
||||||
|
|
||||||
app.manage(Shell {
|
app.manage(Shell {
|
||||||
app: app.clone(),
|
app: app.clone(),
|
||||||
children: Default::default(),
|
children: Default::default(),
|
||||||
open_scope: open_scope(&config.open),
|
|
||||||
|
|
||||||
#[cfg(mobile)]
|
|
||||||
mobile_plugin_handle: handle,
|
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
@@ -143,23 +96,3 @@ pub fn init<R: Runtime>() -> TauriPlugin<R, Option<config::Config>> {
|
|||||||
})
|
})
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_scope(open: &config::ShellAllowlistOpen) -> scope::OpenScope {
|
|
||||||
let shell_scope_open = match open {
|
|
||||||
config::ShellAllowlistOpen::Flag(false) => None,
|
|
||||||
// we want to add a basic regex validation even if the config is not set
|
|
||||||
config::ShellAllowlistOpen::Unset | config::ShellAllowlistOpen::Flag(true) => {
|
|
||||||
Some(Regex::new(r"^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+").unwrap())
|
|
||||||
}
|
|
||||||
config::ShellAllowlistOpen::Validate(validator) => {
|
|
||||||
let regex = format!("^{validator}$");
|
|
||||||
let validator =
|
|
||||||
Regex::new(®ex).unwrap_or_else(|e| panic!("invalid regex {regex}: {e}"));
|
|
||||||
Some(validator)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
scope::OpenScope {
|
|
||||||
open: shell_scope_open,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,138 +0,0 @@
|
|||||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//! Types and functions related to shell.
|
|
||||||
|
|
||||||
use serde::{Deserialize, Deserializer};
|
|
||||||
|
|
||||||
use crate::scope::OpenScope;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
/// Program to use on the [`open()`] call.
|
|
||||||
#[deprecated(since = "2.1.0", note = "Use tauri-plugin-opener instead.")]
|
|
||||||
pub enum Program {
|
|
||||||
/// Use the `open` program.
|
|
||||||
Open,
|
|
||||||
/// Use the `start` program.
|
|
||||||
Start,
|
|
||||||
/// Use the `xdg-open` program.
|
|
||||||
XdgOpen,
|
|
||||||
/// Use the `gio` program.
|
|
||||||
Gio,
|
|
||||||
/// Use the `gnome-open` program.
|
|
||||||
GnomeOpen,
|
|
||||||
/// Use the `kde-open` program.
|
|
||||||
KdeOpen,
|
|
||||||
/// Use the `wslview` program.
|
|
||||||
WslView,
|
|
||||||
/// Use the `Firefox` program.
|
|
||||||
Firefox,
|
|
||||||
/// Use the `Google Chrome` program.
|
|
||||||
Chrome,
|
|
||||||
/// Use the `Chromium` program.
|
|
||||||
Chromium,
|
|
||||||
/// Use the `Safari` program.
|
|
||||||
Safari,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Program {
|
|
||||||
type Err = super::Error;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
let p = match s.to_lowercase().as_str() {
|
|
||||||
"open" => Self::Open,
|
|
||||||
"start" => Self::Start,
|
|
||||||
"xdg-open" => Self::XdgOpen,
|
|
||||||
"gio" => Self::Gio,
|
|
||||||
"gnome-open" => Self::GnomeOpen,
|
|
||||||
"kde-open" => Self::KdeOpen,
|
|
||||||
"wslview" => Self::WslView,
|
|
||||||
"firefox" => Self::Firefox,
|
|
||||||
"chrome" | "google chrome" => Self::Chrome,
|
|
||||||
"chromium" => Self::Chromium,
|
|
||||||
"safari" => Self::Safari,
|
|
||||||
_ => return Err(crate::Error::UnknownProgramName(s.to_string())),
|
|
||||||
};
|
|
||||||
Ok(p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for Program {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let s = String::deserialize(deserializer)?;
|
|
||||||
Program::from_str(&s).map_err(|e| serde::de::Error::custom(e.to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Program {
|
|
||||||
pub(crate) fn name(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
Self::Open => "open",
|
|
||||||
Self::Start => "start",
|
|
||||||
Self::XdgOpen => "xdg-open",
|
|
||||||
Self::Gio => "gio",
|
|
||||||
Self::GnomeOpen => "gnome-open",
|
|
||||||
Self::KdeOpen => "kde-open",
|
|
||||||
Self::WslView => "wslview",
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
Self::Firefox => "Firefox",
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
Self::Firefox => "firefox",
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
Self::Chrome => "Google Chrome",
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
Self::Chrome => "google-chrome",
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
Self::Chromium => "Chromium",
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
Self::Chromium => "chromium",
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
Self::Safari => "Safari",
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
Self::Safari => "safari",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Opens path or URL with the program specified in `with`, or system default if `None`.
|
|
||||||
///
|
|
||||||
/// The path will be matched against the shell open validation regex, defaulting to `^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+`.
|
|
||||||
/// A custom validation regex may be supplied in the config in `plugins > shell > scope > open`.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust,no_run
|
|
||||||
/// use tauri_plugin_shell::ShellExt;
|
|
||||||
/// tauri::Builder::default()
|
|
||||||
/// .setup(|app| {
|
|
||||||
/// // open the given URL on the system default browser
|
|
||||||
/// app.shell().open("https://github.com/tauri-apps/tauri", None)?;
|
|
||||||
/// Ok(())
|
|
||||||
/// });
|
|
||||||
/// ```
|
|
||||||
#[deprecated(since = "2.1.0", note = "Use tauri-plugin-opener instead.")]
|
|
||||||
pub fn open<P: AsRef<str>>(
|
|
||||||
scope: Option<&OpenScope>,
|
|
||||||
path: P,
|
|
||||||
with: Option<Program>,
|
|
||||||
) -> crate::Result<()> {
|
|
||||||
// validate scope if we have any (JS calls)
|
|
||||||
if let Some(scope) = scope {
|
|
||||||
scope.open(path.as_ref(), with).map_err(Into::into)
|
|
||||||
} else {
|
|
||||||
// when running directly from Rust code we don't need to validate the path
|
|
||||||
match with.map(Program::name) {
|
|
||||||
Some(program) => ::open::with_detached(path.as_ref(), program),
|
|
||||||
None => ::open::that_detached(path.as_ref()),
|
|
||||||
}
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,8 +4,6 @@
|
|||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
use crate::open::Program;
|
|
||||||
use crate::process::Command;
|
use crate::process::Command;
|
||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
@@ -139,13 +137,6 @@ impl ScopeAllowedArg {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scope for the open command
|
|
||||||
pub struct OpenScope {
|
|
||||||
/// The validation regex that `shell > open` paths must match against.
|
|
||||||
/// When set to `None`, no values are accepted.
|
|
||||||
pub open: Option<Regex>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Scope for shell process spawning.
|
/// Scope for shell process spawning.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ShellScope<'a> {
|
pub struct ShellScope<'a> {
|
||||||
@@ -198,39 +189,6 @@ pub enum Error {
|
|||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OpenScope {
|
|
||||||
/// Open a path in the default (or specified) browser.
|
|
||||||
///
|
|
||||||
/// The path is validated against the `plugins > shell > open` validation regex, which
|
|
||||||
/// defaults to `^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+`.
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub fn open(&self, path: &str, with: Option<Program>) -> Result<(), Error> {
|
|
||||||
// ensure we pass validation if the configuration has one
|
|
||||||
if let Some(regex) = &self.open {
|
|
||||||
if !regex.is_match(path) {
|
|
||||||
return Err(Error::Validation {
|
|
||||||
index: 0,
|
|
||||||
validation: regex.as_str().into(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log::warn!("open() command called but the plugin configuration denies calls from JavaScript; set `tauri.conf.json > plugins > shell > open` to true or a validation regex string");
|
|
||||||
return Err(Error::Validation {
|
|
||||||
index: 0,
|
|
||||||
validation: "tauri^".to_string(), // purposefully impossible regex
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// The prevention of argument escaping is handled by the usage of std::process::Command::arg by
|
|
||||||
// the `open` dependency. This behavior should be re-confirmed during upgrades of `open`.
|
|
||||||
match with.map(Program::name) {
|
|
||||||
Some(program) => ::open::with_detached(path, program),
|
|
||||||
None => ::open::that_detached(path),
|
|
||||||
}
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ShellScope<'_> {
|
impl ShellScope<'_> {
|
||||||
/// Validates argument inputs and creates a Tauri sidecar [`Command`].
|
/// Validates argument inputs and creates a Tauri sidecar [`Command`].
|
||||||
pub fn prepare_sidecar(
|
pub fn prepare_sidecar(
|
||||||
|
|||||||
Reference in New Issue
Block a user