refactor(api)!: change window label getters to be async ref #5380 (#10630)

This commit is contained in:
Lucas Fernandes Nogueira 2024-08-15 09:12:40 -03:00 committed by GitHub
parent b160f9359d
commit b6dca99fff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 153 additions and 181 deletions

View File

@ -0,0 +1,9 @@
---
"@tauri-apps/api": patch:breaking
---
Changed `WebviewWindow.getAll`, `WebviewWindow.getByLabel`, `getAllWebviewWindows`,
`Window.getAll`, `Window.getByLabel`, `getAllWindows`,
`Webview.getAll`, `Webview.getByLabel`, `getAllWebviews`
to be async so their return value are synchronized with the state from the Rust side,
meaning new and destroyed windows are reflected.

View File

@ -46,6 +46,7 @@ const PLUGINS: &[(&str, &[(&str, bool)])] = &[
&[
("create", false),
// getters
("get_all_windows", true),
("scale_factor", true),
("inner_position", true),
("outer_position", true),
@ -120,6 +121,7 @@ const PLUGINS: &[(&str, &[(&str, bool)])] = &[
("create_webview", false),
("create_webview_window", false),
// getters
("get_all_webviews", true),
("webview_position", true),
("webview_size", true),
// setters

View File

@ -2,6 +2,7 @@
Default permissions for the plugin.
- `allow-get-all-webviews`
- `allow-webview-position`
- `allow-webview-size`
- `allow-internal-toggle-devtools`
@ -70,6 +71,32 @@ Denies the create_webview_window command without any pre-configured scope.
<tr>
<td>
`core:webview:allow-get-all-webviews`
</td>
<td>
Enables the get_all_webviews command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`core:webview:deny-get-all-webviews`
</td>
<td>
Denies the get_all_webviews command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`core:webview:allow-internal-toggle-devtools`
</td>

View File

@ -2,6 +2,7 @@
Default permissions for the plugin.
- `allow-get-all-windows`
- `allow-scale-factor`
- `allow-inner-position`
- `allow-outer-position`
@ -220,6 +221,32 @@ Denies the destroy command without any pre-configured scope.
<tr>
<td>
`core:window:allow-get-all-windows`
</td>
<td>
Enables the get_all_windows command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`core:window:deny-get-all-windows`
</td>
<td>
Denies the get_all_windows command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`core:window:allow-hide`
</td>

File diff suppressed because one or more lines are too long

View File

@ -8,10 +8,7 @@ use crate::{
channel::ChannelDataIpcQueue, CallbackFn, CommandArg, CommandItem, Invoke, InvokeError,
InvokeHandler, InvokeResponder, InvokeResponse,
},
manager::{
webview::{UriSchemeProtocol, WebviewLabelDef},
AppManager, Asset,
},
manager::{webview::UriSchemeProtocol, AppManager, Asset},
plugin::{Plugin, PluginStore},
resources::ResourceTable,
runtime::{
@ -1961,24 +1958,8 @@ impl<R: Runtime> HasDisplayHandle for App<R> {
fn setup<R: Runtime>(app: &mut App<R>) -> crate::Result<()> {
app.ran_setup = true;
let window_labels = app
.config()
.app
.windows
.iter()
.map(|p| p.label.clone())
.collect::<Vec<_>>();
let webview_labels = window_labels
.iter()
.map(|label| WebviewLabelDef {
window_label: label.clone(),
label: label.clone(),
})
.collect::<Vec<_>>();
for window_config in app.config().app.windows.clone() {
WebviewWindowBuilder::from_config(app.handle(), &window_config)?
.build_internal(&window_labels, &webview_labels)?;
WebviewWindowBuilder::from_config(app.handle(), &window_config)?.build()?;
}
app.manager.assets.setup(app);

View File

@ -64,13 +64,6 @@ pub struct UriSchemeProtocol<R: Runtime> {
Box<dyn Fn(&AppHandle<R>, http::Request<Vec<u8>>, UriSchemeResponder) + Send + Sync>,
}
#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WebviewLabelDef {
pub window_label: String,
pub label: String,
}
pub struct WebviewManager<R: Runtime> {
pub webviews: Mutex<HashMap<String, Webview<R>>>,
/// The JS message handler.
@ -127,8 +120,6 @@ impl<R: Runtime> WebviewManager<R> {
mut pending: PendingWebview<EventLoopMessage, R>,
label: &str,
window_label: &str,
window_labels: &[String],
webview_labels: &[WebviewLabelDef],
manager: &M,
) -> crate::Result<PendingWebview<EventLoopMessage, R>> {
let app_manager = manager.manager();
@ -156,13 +147,6 @@ impl<R: Runtime> WebviewManager<R> {
}
.render_default(&Default::default())?;
let mut webview_labels = webview_labels.to_vec();
if !webview_labels.iter().any(|w| w.label == label) {
webview_labels.push(WebviewLabelDef {
window_label: window_label.to_string(),
label: label.to_string(),
});
}
webview_attributes = webview_attributes
.initialization_script(
r"
@ -184,15 +168,11 @@ impl<R: Runtime> WebviewManager<R> {
r#"
Object.defineProperty(window.__TAURI_INTERNALS__, 'metadata', {{
value: {{
windows: {window_labels_array}.map(function (label) {{ return {{ label: label }} }}),
webviews: {webview_labels_array},
currentWindow: {{ label: {current_window_label} }},
currentWebview: {{ label: {current_webview_label} }}
}}
}})
"#,
window_labels_array = serde_json::to_string(&window_labels)?,
webview_labels_array = serde_json::to_string(&webview_labels)?,
current_window_label = serde_json::to_string(window_label)?,
current_webview_label = serde_json::to_string(&label)?,
))
@ -415,8 +395,6 @@ impl<R: Runtime> WebviewManager<R> {
manager: &M,
mut pending: PendingWebview<EventLoopMessage, R>,
window_label: &str,
window_labels: &[String],
webview_labels: &[WebviewLabelDef],
) -> crate::Result<PendingWebview<EventLoopMessage, R>> {
if self.webviews_lock().contains_key(&pending.label) {
return Err(crate::Error::WebviewLabelAlreadyExists(pending.label));
@ -509,14 +487,7 @@ impl<R: Runtime> WebviewManager<R> {
}
let label = pending.label.clone();
pending = self.prepare_pending_webview(
pending,
&label,
window_label,
window_labels,
webview_labels,
manager,
)?;
pending = self.prepare_pending_webview(pending, &label, window_label, manager)?;
pending.ipc_handler = Some(crate::ipc::protocol::message_handler(
manager.manager_owned(),
@ -638,12 +609,6 @@ impl<R: Runtime> WebviewManager<R> {
.expect("failed to run on_webview_created hook");
}
if let Ok(webview_labels_array) = serde_json::to_string(&webview.manager.webview.labels()) {
let _ = webview.manager.webview.eval_script_all(format!(
"window.__TAURI_INTERNALS__.metadata.webviews = {webview_labels_array}.map(function (label) {{ return {{ label: label }} }})",
));
}
let _ = webview.manager.emit(
"tauri://webview-created",
Some(crate::webview::CreatedEvent {

View File

@ -162,13 +162,6 @@ fn on_window_event<R: Runtime>(window: &Window<R>, event: &WindowEvent) -> crate
}
WindowEvent::Destroyed => {
window.emit_to_window(WINDOW_DESTROYED_EVENT, ())?;
let label = window.label();
if let Ok(webview_labels_array) = serde_json::to_string(&window.manager().webview.labels()) {
let _ = window.manager().webview.eval_script_all(format!(
r#"(function () {{ const metadata = window.__TAURI_INTERNALS__.metadata; if (metadata != null) {{ metadata.windows = window.__TAURI_INTERNALS__.metadata.windows.filter(w => w.label !== "{label}"); metadata.webviews = {webview_labels_array}.map(function (label) {{ return {{ label: label }} }}) }} }})()"#,
));
}
}
WindowEvent::Focused(focused) => window.emit_to_window(
if *focused {

View File

@ -32,7 +32,7 @@ use crate::{
CallbackFn, CommandArg, CommandItem, Invoke, InvokeBody, InvokeError, InvokeMessage,
InvokeResolver, Origin, OwnedInvokeResponder,
},
manager::{webview::WebviewLabelDef, AppManager},
manager::AppManager,
sealed::{ManagerBase, RuntimeOrDispatch},
AppHandle, Emitter, Event, EventId, EventLoopMessage, Listener, Manager, ResourceTable, Runtime,
Window,
@ -548,8 +548,6 @@ tauri::Builder::default()
mut self,
manager: &M,
window_label: &str,
window_labels: &[String],
webview_labels: &[WebviewLabelDef],
) -> crate::Result<PendingWebview<EventLoopMessage, R>> {
let mut pending = PendingWebview::new(self.webview_attributes, self.label.clone())?;
pending.navigation_handler = self.navigation_handler.take();
@ -589,13 +587,10 @@ tauri::Builder::default()
}
}));
manager.manager().webview.prepare_webview(
manager,
pending,
window_label,
window_labels,
webview_labels,
)
manager
.manager()
.webview
.prepare_webview(manager, pending, window_label)
}
/// Creates a new webview on the given window.
@ -606,27 +601,9 @@ tauri::Builder::default()
position: Position,
size: Size,
) -> crate::Result<Webview<R>> {
let window_labels = window
.manager()
.window
.labels()
.into_iter()
.collect::<Vec<_>>();
let webview_labels = window
.manager()
.webview
.webviews_lock()
.values()
.map(|w| WebviewLabelDef {
window_label: w.window().label().to_string(),
label: w.label().to_string(),
})
.collect::<Vec<_>>();
let app_manager = window.manager();
let mut pending =
self.into_pending_webview(&window, window.label(), &window_labels, &webview_labels)?;
let mut pending = self.into_pending_webview(&window, window.label())?;
pending.webview_attributes.bounds = Some(Rect { size, position });

View File

@ -12,7 +12,7 @@ use crate::{
#[cfg(desktop)]
mod desktop_commands {
use serde::Deserialize;
use serde::{Deserialize, Serialize};
use tauri_runtime::dpi::{Position, Size};
use tauri_utils::config::{WebviewUrl, WindowConfig};
@ -44,6 +44,25 @@ mod desktop_commands {
zoom_hotkeys_enabled: bool,
}
#[derive(Serialize)]
pub struct WebviewRef {
window_label: String,
label: String,
}
#[command(root = "crate")]
pub async fn get_all_webviews<R: Runtime>(app: AppHandle<R>) -> Vec<WebviewRef> {
app
.manager()
.webviews()
.values()
.map(|webview| WebviewRef {
window_label: webview.window().label().into(),
label: webview.label().into(),
})
.collect()
}
#[command(root = "crate")]
pub async fn create_webview_window<R: Runtime>(
app: AppHandle<R>,
@ -232,6 +251,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
desktop_commands::create_webview,
desktop_commands::create_webview_window,
// getters
desktop_commands::get_all_webviews,
desktop_commands::webview_position,
desktop_commands::webview_size,
// setters

View File

@ -33,7 +33,7 @@ use url::Url;
use crate::{
ipc::{CommandArg, CommandItem, InvokeError, OwnedInvokeResponder},
manager::{webview::WebviewLabelDef, AppManager},
manager::AppManager,
sealed::{ManagerBase, RuntimeOrDispatch},
webview::PageLoadPayload,
webview::WebviewBuilder,
@ -347,19 +347,6 @@ tauri::Builder::default()
let (_window, webview) = self.window_builder.with_webview(self.webview_builder)?;
Ok(WebviewWindow { webview })
}
pub(crate) fn build_internal(
self,
window_labels: &[String],
webview_labels: &[WebviewLabelDef],
) -> crate::Result<WebviewWindow<R>> {
let (_window, webview) = self.window_builder.with_webview_internal(
self.webview_builder,
window_labels,
webview_labels,
)?;
Ok(WebviewWindow { webview })
}
}
/// Desktop APIs.

View File

@ -20,7 +20,7 @@ use crate::{
app::AppHandle,
event::{Event, EventId, EventTarget},
ipc::{CommandArg, CommandItem, InvokeError},
manager::{webview::WebviewLabelDef, AppManager},
manager::AppManager,
runtime::{
monitor::Monitor as RuntimeMonitor,
window::{DetachedWindow, PendingWindow, WindowBuilder as _},
@ -324,36 +324,7 @@ tauri::Builder::default()
self,
webview: WebviewBuilder<R>,
) -> crate::Result<(Window<R>, Webview<R>)> {
let window_labels = self
.manager
.manager()
.window
.labels()
.into_iter()
.collect::<Vec<_>>();
let webview_labels = self
.manager
.manager()
.webview
.webviews_lock()
.values()
.map(|w| WebviewLabelDef {
window_label: w.window().label().to_string(),
label: w.label().to_string(),
})
.collect::<Vec<_>>();
self.with_webview_internal(webview, &window_labels, &webview_labels)
}
pub(crate) fn with_webview_internal(
self,
webview: WebviewBuilder<R>,
window_labels: &[String],
webview_labels: &[WebviewLabelDef],
) -> crate::Result<(Window<R>, Webview<R>)> {
let pending_webview =
webview.into_pending_webview(self.manager, &self.label, window_labels, webview_labels)?;
let pending_webview = webview.into_pending_webview(self.manager, &self.label)?;
let window = self.build_internal(Some(pending_webview))?;
let webview = window.webviews().first().unwrap().clone();
@ -430,11 +401,6 @@ tauri::Builder::default()
let window_label = window.label().to_string();
// run on the main thread to fix a deadlock on webview.eval if the tracing feature is enabled
let _ = window.run_on_main_thread(move || {
let _ = app_manager.webview.eval_script_all(format!(
"window.__TAURI_INTERNALS__.metadata.windows = {window_labels_array}.map(function (label) {{ return {{ label: label }} }})",
window_labels_array = serde_json::to_string(&app_manager.window.labels()).unwrap(),
));
let _ = app_manager.emit(
"tauri://window-created",
Some(crate::webview::CreatedEvent {

View File

@ -24,6 +24,11 @@ mod desktop_commands {
UserAttentionType, Webview, Window,
};
#[command(root = "crate")]
pub async fn get_all_windows<R: Runtime>(app: AppHandle<R>) -> Vec<String> {
app.manager().windows().keys().cloned().collect()
}
#[command(root = "crate")]
pub async fn create<R: Runtime>(app: AppHandle<R>, options: WindowConfig) -> crate::Result<()> {
WindowBuilder::from_config(&app, &options)?.build()?;
@ -218,6 +223,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
Box::new(crate::generate_handler![
desktop_commands::create,
// getters
desktop_commands::get_all_windows,
desktop_commands::scale_factor,
desktop_commands::inner_position,
desktop_commands::outer_position,

View File

@ -1,5 +1,5 @@
### Permission Table
## Permission Table
<table>
<tr>

View File

@ -60,13 +60,24 @@ function getCurrentWebview(): Webview {
*
* @since 2.0.0
*/
function getAllWebviews(): Webview[] {
return window.__TAURI_INTERNALS__.metadata.webviews.map(
(w) =>
new Webview(Window.getByLabel(w.windowLabel)!, w.label, {
// @ts-expect-error `skip` is not defined in the public API but it is handled by the constructor
skip: true
})
async function getAllWebviews(): Promise<Webview[]> {
return invoke<Array<{ windowLabel: string; label: string }>>(
'plugin:webview|get_all_webviews'
).then((webviews) =>
webviews.map(
(w) =>
new Webview(
new Window(w.windowLabel, {
// @ts-expect-error `skip` is not defined in the public API but it is handled by the constructor
skip: true
}),
w.label,
{
// @ts-expect-error `skip` is not defined in the public API but it is handled by the constructor
skip: true
}
)
)
)
}
@ -174,8 +185,8 @@ class Webview {
* @param label The webview label.
* @returns The Webview instance to communicate with the webview or null if the webview doesn't exist.
*/
static getByLabel(label: string): Webview | null {
return getAllWebviews().find((w) => w.label === label) ?? null
static async getByLabel(label: string): Promise<Webview | null> {
return (await getAllWebviews()).find((w) => w.label === label) ?? null
}
/**
@ -188,7 +199,7 @@ class Webview {
/**
* Gets a list of instances of `Webview` for all available webviews.
*/
static getAll(): Webview[] {
static async getAll(): Promise<Webview[]> {
return getAllWebviews()
}

View File

@ -31,13 +31,15 @@ function getCurrentWebviewWindow(): WebviewWindow {
*
* @since 2.0.0
*/
function getAllWebviewWindows(): WebviewWindow[] {
return window.__TAURI_INTERNALS__.metadata.webviews.map(
(w) =>
new WebviewWindow(w.label, {
// @ts-expect-error `skip` is not defined in the public API but it is handled by the constructor
skip: true
})
async function getAllWebviewWindows(): Promise<WebviewWindow[]> {
return invoke<string[]>('plugin:window|get_all_windows').then((windows) =>
windows.map(
(w) =>
new WebviewWindow(w, {
// @ts-expect-error `skip` is not defined in the public API but it is handled by the constructor
skip: true
})
)
)
}
@ -107,9 +109,9 @@ class WebviewWindow {
* @param label The webview label.
* @returns The Webview instance to communicate with the webview or null if the webview doesn't exist.
*/
static getByLabel(label: string): WebviewWindow | null {
static async getByLabel(label: string): Promise<WebviewWindow | null> {
const webview =
getAllWebviewWindows().find((w) => w.label === label) ?? null
(await getAllWebviewWindows()).find((w) => w.label === label) ?? null
if (webview) {
// @ts-expect-error `skip` is not defined in the public API but it is handled by the constructor
return new WebviewWindow(webview.label, { skip: true })
@ -127,11 +129,8 @@ class WebviewWindow {
/**
* Gets a list of instances of `Webview` for all available webviews.
*/
static getAll(): WebviewWindow[] {
return getAllWebviewWindows().map(
// @ts-expect-error `skip` is not defined in the public API but it is handled by the constructor
(w) => new WebviewWindow(w.label, { skip: true })
)
static async getAll(): Promise<WebviewWindow[]> {
return getAllWebviewWindows()
}
/**

View File

@ -218,13 +218,15 @@ function getCurrentWindow(): Window {
*
* @since 1.0.0
*/
function getAllWindows(): Window[] {
return window.__TAURI_INTERNALS__.metadata.windows.map(
(w) =>
new Window(w.label, {
// @ts-expect-error `skip` is not defined in the public API but it is handled by the constructor
skip: true
})
async function getAllWindows(): Promise<Window[]> {
return invoke<string[]>('plugin:window|get_all_windows').then((windows) =>
windows.map(
(w) =>
new Window(w, {
// @ts-expect-error `skip` is not defined in the public API but it is handled by the constructor
skip: true
})
)
)
}
@ -319,8 +321,8 @@ class Window {
* @param label The window label.
* @returns The Window instance to communicate with the window or null if the window doesn't exist.
*/
static getByLabel(label: string): Window | null {
return getAllWindows().find((w) => w.label === label) ?? null
static async getByLabel(label: string): Promise<Window | null> {
return (await getAllWindows()).find((w) => w.label === label) ?? null
}
/**
@ -333,7 +335,7 @@ class Window {
/**
* Gets a list of instances of `Window` for all available windows.
*/
static getAll(): Window[] {
static async getAll(): Promise<Window[]> {
return getAllWindows()
}
@ -348,7 +350,7 @@ class Window {
* @returns The Window instance or `undefined` if there is not any focused window.
*/
static async getFocusedWindow(): Promise<Window | null> {
for (const w of getAllWindows()) {
for (const w of await getAllWindows()) {
if (await w.isFocused()) {
return w
}