Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/on-new-window-requested-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@tauri-apps/api": minor:feat
---

Added `onNewWindow` option to configure whether to allow the webview to open URLs when `window.open` is used.

6 changes: 6 additions & 0 deletions .changes/on-new-window-requested-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"tauri": minor:feat
"tauri-utils": minor:feat
---

Added `WindowConfig::on_new_window` to statically configure `WebviewBuilder::on_new_window`.
100 changes: 100 additions & 0 deletions crates/tauri-cli/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,17 @@
"$ref": "#/definitions/ScrollBarStyle"
}
]
},
"onNewWindow": {
"description": "Action to perform when a new window is requested to be created.",
"default": {
"action": "deny"
},
"allOf": [
{
"$ref": "#/definitions/OnNewWindow"
}
]
}
},
"additionalProperties": false
Expand Down Expand Up @@ -1140,6 +1151,95 @@
}
]
},
"OnNewWindow": {
"description": "Action to perform when a new window is requested to be created.",
"oneOf": [
{
"description": "Allow the window to be created using the default webview implementation.",
"type": "object",
"required": [
"action"
],
"properties": {
"action": {
"type": "string",
"enum": [
"allowDefault"
]
},
"urls": {
"description": "Only allow URLs matching the given pattern list when set.\n\n By default it is a glob pattern, but can use the full [URLPattern] spec if the `url-pattern` feature is enabled.\n\n [URLPattern]: https://developer.mozilla.org/en-US/docs/Web/API/URLPattern",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/UrlScope"
}
}
},
"additionalProperties": false
},
{
"description": "Allow the window to be created using a Tauri window.",
"type": "object",
"required": [
"action"
],
"properties": {
"action": {
"type": "string",
"enum": [
"allowTauriWindow"
]
},
"urls": {
"description": "Only allow URLs matching the given pattern list when set.\n\n By default it is a glob pattern, but can use the full [URLPattern] spec if the `url-pattern` feature is enabled.\n\n [URLPattern]: https://developer.mozilla.org/en-US/docs/Web/API/URLPattern",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/UrlScope"
}
}
},
"additionalProperties": false
},
{
"description": "Deny the window from being created.",
"type": "object",
"required": [
"action"
],
"properties": {
"action": {
"type": "string",
"enum": [
"deny"
]
}
},
"additionalProperties": false
}
]
},
"UrlScope": {
"description": "A scope to match URLs.",
"anyOf": [
{
"description": "A [`GlobPattern`] to match URLs.",
"allOf": [
{
"$ref": "#/definitions/GlobPattern"
}
]
}
]
},
"GlobPattern": {
"type": "string"
},
"SecurityConfig": {
"description": "Security configuration.\n\n See more: <https://v2.tauri.app/reference/config/#securityconfig>",
"type": "object",
Expand Down
100 changes: 100 additions & 0 deletions crates/tauri-schema-generator/schemas/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,17 @@
"$ref": "#/definitions/ScrollBarStyle"
}
]
},
"onNewWindow": {
"description": "Action to perform when a new window is requested to be created.",
"default": {
"action": "deny"
},
"allOf": [
{
"$ref": "#/definitions/OnNewWindow"
}
]
}
},
"additionalProperties": false
Expand Down Expand Up @@ -1140,6 +1151,95 @@
}
]
},
"OnNewWindow": {
"description": "Action to perform when a new window is requested to be created.",
"oneOf": [
{
"description": "Allow the window to be created using the default webview implementation.",
"type": "object",
"required": [
"action"
],
"properties": {
"action": {
"type": "string",
"enum": [
"allowDefault"
]
},
"urls": {
"description": "Only allow URLs matching the given pattern list when set.\n\n By default it is a glob pattern, but can use the full [URLPattern] spec if the `url-pattern` feature is enabled.\n\n [URLPattern]: https://developer.mozilla.org/en-US/docs/Web/API/URLPattern",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/UrlScope"
}
}
},
"additionalProperties": false
},
{
"description": "Allow the window to be created using a Tauri window.",
"type": "object",
"required": [
"action"
],
"properties": {
"action": {
"type": "string",
"enum": [
"allowTauriWindow"
]
},
"urls": {
"description": "Only allow URLs matching the given pattern list when set.\n\n By default it is a glob pattern, but can use the full [URLPattern] spec if the `url-pattern` feature is enabled.\n\n [URLPattern]: https://developer.mozilla.org/en-US/docs/Web/API/URLPattern",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/UrlScope"
}
}
},
"additionalProperties": false
},
{
"description": "Deny the window from being created.",
"type": "object",
"required": [
"action"
],
"properties": {
"action": {
"type": "string",
"enum": [
"deny"
]
}
},
"additionalProperties": false
}
]
},
"UrlScope": {
"description": "A scope to match URLs.",
"anyOf": [
{
"description": "A [`GlobPattern`] to match URLs.",
"allOf": [
{
"$ref": "#/definitions/GlobPattern"
}
]
}
]
},
"GlobPattern": {
"type": "string"
},
"SecurityConfig": {
"description": "Security configuration.\n\n See more: <https://v2.tauri.app/reference/config/#securityconfig>",
"type": "object",
Expand Down
1 change: 1 addition & 0 deletions crates/tauri-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,4 @@ config-json5 = ["json5"]
config-toml = []
resources = ["walkdir"]
html-manipulation = ["dep:html5ever", "dep:kuchiki"]
url-pattern = []
104 changes: 2 additions & 102 deletions crates/tauri-utils/src/acl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,13 @@ use std::{
fs,
num::NonZeroU64,
path::PathBuf,
str::FromStr,
sync::Arc,
};
use thiserror::Error;
use url::Url;

use crate::{
config::{CapabilityEntry, Config},
platform::Target,
url::UrlPattern,
};

pub use self::{identifier::*, value::*};
Expand Down Expand Up @@ -273,64 +271,6 @@ pub struct PermissionSet {
pub permissions: Vec<String>,
}

/// UrlPattern for [`ExecutionContext::Remote`].
#[derive(Debug, Clone)]
pub struct RemoteUrlPattern(Arc<urlpattern::UrlPattern>, String);

impl FromStr for RemoteUrlPattern {
type Err = urlpattern::quirks::Error;

fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
let mut init = urlpattern::UrlPatternInit::parse_constructor_string::<regex::Regex>(s, None)?;
if init.search.as_ref().map(|p| p.is_empty()).unwrap_or(true) {
init.search.replace("*".to_string());
}
if init.hash.as_ref().map(|p| p.is_empty()).unwrap_or(true) {
init.hash.replace("*".to_string());
}
if init
.pathname
.as_ref()
.map(|p| p.is_empty() || p == "/")
.unwrap_or(true)
{
init.pathname.replace("*".to_string());
}
let pattern = urlpattern::UrlPattern::parse(init, Default::default())?;
Ok(Self(Arc::new(pattern), s.to_string()))
}
}

impl RemoteUrlPattern {
#[doc(hidden)]
pub fn as_str(&self) -> &str {
&self.1
}

/// Test if a given URL matches the pattern.
pub fn test(&self, url: &Url) -> bool {
self
.0
.test(urlpattern::UrlPatternMatchInput::Url(url.clone()))
.unwrap_or_default()
}
}

impl PartialEq for RemoteUrlPattern {
fn eq(&self, other: &Self) -> bool {
self.0.protocol() == other.0.protocol()
&& self.0.username() == other.0.username()
&& self.0.password() == other.0.password()
&& self.0.hostname() == other.0.hostname()
&& self.0.port() == other.0.port()
&& self.0.pathname() == other.0.pathname()
&& self.0.search() == other.0.search()
&& self.0.hash() == other.0.hash()
}
}

impl Eq for RemoteUrlPattern {}

/// Execution context of an IPC call.
#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub enum ExecutionContext {
Expand All @@ -340,7 +280,7 @@ pub enum ExecutionContext {
/// Remote URL is trying to use the IPC.
Remote {
/// The URL trying to access the IPC (URL pattern).
url: RemoteUrlPattern,
url: UrlPattern,
},
}

Expand Down Expand Up @@ -420,46 +360,6 @@ pub fn read_allowed_commands() -> Option<AllowedCommands> {
Some(json)
}

#[cfg(test)]
mod tests {
use crate::acl::RemoteUrlPattern;

#[test]
fn url_pattern_domain_wildcard() {
let pattern: RemoteUrlPattern = "http://*".parse().unwrap();

assert!(pattern.test(&"http://tauri.app/path".parse().unwrap()));
assert!(pattern.test(&"http://tauri.app/path?q=1".parse().unwrap()));

assert!(pattern.test(&"http://localhost/path".parse().unwrap()));
assert!(pattern.test(&"http://localhost/path?q=1".parse().unwrap()));

let pattern: RemoteUrlPattern = "http://*.tauri.app".parse().unwrap();

assert!(!pattern.test(&"http://tauri.app/path".parse().unwrap()));
assert!(!pattern.test(&"http://tauri.app/path?q=1".parse().unwrap()));
assert!(pattern.test(&"http://api.tauri.app/path".parse().unwrap()));
assert!(pattern.test(&"http://api.tauri.app/path?q=1".parse().unwrap()));
assert!(!pattern.test(&"http://localhost/path".parse().unwrap()));
assert!(!pattern.test(&"http://localhost/path?q=1".parse().unwrap()));
}

#[test]
fn url_pattern_path_wildcard() {
let pattern: RemoteUrlPattern = "http://localhost/*".parse().unwrap();
assert!(pattern.test(&"http://localhost/path".parse().unwrap()));
assert!(pattern.test(&"http://localhost/path?q=1".parse().unwrap()));
}

#[test]
fn url_pattern_scheme_wildcard() {
let pattern: RemoteUrlPattern = "*://localhost".parse().unwrap();
assert!(pattern.test(&"http://localhost/path".parse().unwrap()));
assert!(pattern.test(&"https://localhost/path?q=1".parse().unwrap()));
assert!(pattern.test(&"custom://localhost/path".parse().unwrap()));
}
}

#[cfg(feature = "build")]
mod build_ {
use std::convert::identity;
Expand Down
Loading