mirror of
https://github.com/bitwarden/browser
synced 2026-02-26 09:33:22 +00:00
POC for autotype
This commit is contained in:
159
apps/desktop/desktop_native/Cargo.lock
generated
159
apps/desktop/desktop_native/Cargo.lock
generated
@@ -162,7 +162,7 @@ dependencies = [
|
||||
"serde_repr",
|
||||
"tokio",
|
||||
"url",
|
||||
"zbus 5.6.0",
|
||||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -362,6 +362,15 @@ version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "autotype"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"windows 0.61.1",
|
||||
"windows-core 0.61.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.75"
|
||||
@@ -900,7 +909,7 @@ dependencies = [
|
||||
"widestring",
|
||||
"windows 0.61.1",
|
||||
"windows-future",
|
||||
"zbus 4.4.0",
|
||||
"zbus",
|
||||
"zbus_polkit",
|
||||
"zeroizing-alloc",
|
||||
]
|
||||
@@ -910,6 +919,7 @@ name = "desktop_napi"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"autotype",
|
||||
"base64",
|
||||
"desktop_core",
|
||||
"hex",
|
||||
@@ -2063,10 +2073,10 @@ dependencies = [
|
||||
"sha2",
|
||||
"subtle",
|
||||
"tokio",
|
||||
"zbus 5.6.0",
|
||||
"zbus_macros 5.6.0",
|
||||
"zbus",
|
||||
"zbus_macros",
|
||||
"zeroize",
|
||||
"zvariant 5.5.1",
|
||||
"zvariant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2715,17 +2725,6 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.8"
|
||||
@@ -3921,9 +3920,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zbus"
|
||||
version = "4.4.0"
|
||||
version = "5.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725"
|
||||
checksum = "59c333f648ea1b647bc95dc1d34807c8e25ed7a6feff3394034dc4776054b236"
|
||||
dependencies = [
|
||||
"async-broadcast",
|
||||
"async-executor",
|
||||
@@ -3938,90 +3937,37 @@ dependencies = [
|
||||
"enumflags2",
|
||||
"event-listener",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"hex",
|
||||
"nix",
|
||||
"ordered-stream",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"sha1",
|
||||
"static_assertions",
|
||||
"tracing",
|
||||
"uds_windows",
|
||||
"windows-sys 0.52.0",
|
||||
"xdg-home",
|
||||
"zbus_macros 4.4.0",
|
||||
"zbus_names 3.0.0",
|
||||
"zvariant 4.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus"
|
||||
version = "5.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2522b82023923eecb0b366da727ec883ace092e7887b61d3da5139f26b44da58"
|
||||
dependencies = [
|
||||
"async-broadcast",
|
||||
"async-recursion",
|
||||
"async-trait",
|
||||
"enumflags2",
|
||||
"event-listener",
|
||||
"futures-core",
|
||||
"futures-lite",
|
||||
"hex",
|
||||
"nix",
|
||||
"ordered-stream",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"static_assertions",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"uds_windows",
|
||||
"windows-sys 0.59.0",
|
||||
"winnow",
|
||||
"zbus_macros 5.6.0",
|
||||
"zbus_names 4.2.0",
|
||||
"zvariant 5.5.1",
|
||||
"xdg-home",
|
||||
"zbus_macros",
|
||||
"zbus_names",
|
||||
"zvariant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus_macros"
|
||||
version = "4.4.0"
|
||||
version = "5.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e"
|
||||
checksum = "f325ad10eb0d0a3eb060203494c3b7ec3162a01a59db75d2deee100339709fc0"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"zvariant_utils 2.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus_macros"
|
||||
version = "5.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05d2e12843c75108c00c618c2e8ef9675b50b6ec095b36dc965f2e5aed463c15"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"zbus_names 4.2.0",
|
||||
"zvariant 5.5.1",
|
||||
"zvariant_utils 3.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus_names"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"zvariant 4.2.0",
|
||||
"zbus_names",
|
||||
"zvariant",
|
||||
"zvariant_utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4033,20 +3979,20 @@ dependencies = [
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"winnow",
|
||||
"zvariant 5.5.1",
|
||||
"zvariant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus_polkit"
|
||||
version = "4.0.0"
|
||||
version = "5.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00a29bfa927b29f91b7feb4e1990f2dd1b4604072f493dc2f074cf59e4e0ba90"
|
||||
checksum = "ad23d5c4d198c7e2641b33e6e0d1f866f117408ba66fe80bbe52e289eeb77c52"
|
||||
dependencies = [
|
||||
"enumflags2",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"static_assertions",
|
||||
"zbus 4.4.0",
|
||||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4149,19 +4095,6 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant"
|
||||
version = "4.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe"
|
||||
dependencies = [
|
||||
"endi",
|
||||
"enumflags2",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"zvariant_derive 4.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant"
|
||||
version = "5.5.1"
|
||||
@@ -4173,21 +4106,8 @@ dependencies = [
|
||||
"serde",
|
||||
"url",
|
||||
"winnow",
|
||||
"zvariant_derive 5.5.1",
|
||||
"zvariant_utils 3.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant_derive"
|
||||
version = "4.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"zvariant_utils 2.1.0",
|
||||
"zvariant_derive",
|
||||
"zvariant_utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4200,18 +4120,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"zvariant_utils 3.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant_utils"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"zvariant_utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["napi", "core", "proxy", "macos_provider", "windows_plugin_authenticator"]
|
||||
members = ["napi", "core", "proxy", "macos_provider", "windows_plugin_authenticator", "autotype"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.0.0"
|
||||
|
||||
13
apps/desktop/desktop_native/autotype/Cargo.toml
Normal file
13
apps/desktop/desktop_native/autotype/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "autotype"
|
||||
version = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
license = { workspace = true }
|
||||
publish = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows = { workspace = true, features = ["Win32_Foundation", "Win32_UI_WindowsAndMessaging" ] }
|
||||
windows-core = { workspace = true }
|
||||
164
apps/desktop/desktop_native/autotype/src/lib.rs
Normal file
164
apps/desktop/desktop_native/autotype/src/lib.rs
Normal file
@@ -0,0 +1,164 @@
|
||||
#![cfg(target_os = "windows")]
|
||||
|
||||
use anyhow::Result;
|
||||
use std::ffi::OsString;
|
||||
use std::os::windows::ffi::OsStringExt;
|
||||
use windows::Win32::Foundation::{HWND, LPARAM};
|
||||
use windows::Win32::UI::Input::KeyboardAndMouse::{
|
||||
SendInput, INPUT, INPUT_KEYBOARD, KEYBDINPUT, KEYEVENTF_KEYUP, VIRTUAL_KEY, VK_RETURN, VK_TAB,
|
||||
};
|
||||
use windows::Win32::UI::WindowsAndMessaging::{
|
||||
EnumWindows, GetForegroundWindow, GetWindowTextW, IsWindowVisible, SetForegroundWindow, ShowWindow, SW_NORMAL,
|
||||
};
|
||||
use windows_core::BOOL;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WindowInfo {
|
||||
pub handle: isize,
|
||||
pub title: String,
|
||||
}
|
||||
|
||||
pub fn get_active_windows() -> Result<Vec<WindowInfo>> {
|
||||
extern "system" fn enum_windows_proc(hwnd: HWND, lparam: LPARAM) -> BOOL {
|
||||
unsafe {
|
||||
if IsWindowVisible(hwnd).as_bool() {
|
||||
let mut buf = [0u16; 512];
|
||||
let len = GetWindowTextW(hwnd, &mut buf);
|
||||
if len > 0 {
|
||||
let title = OsString::from_wide(&buf[..len as usize])
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
let windows = &mut *(lparam.0 as *mut Vec<WindowInfo>);
|
||||
windows.push(WindowInfo {
|
||||
handle: hwnd.0 as isize,
|
||||
title,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
true.into()
|
||||
}
|
||||
|
||||
let mut windows: Vec<WindowInfo> = Vec::new();
|
||||
unsafe {
|
||||
let _ = EnumWindows(
|
||||
Some(enum_windows_proc),
|
||||
LPARAM(&mut windows as *mut _ as isize),
|
||||
);
|
||||
}
|
||||
Ok(windows)
|
||||
}
|
||||
|
||||
pub fn set_window_foreground(handle: isize) -> Result<()> {
|
||||
unsafe {
|
||||
let hwnd = HWND(handle as *mut std::ffi::c_void);
|
||||
let _ = ShowWindow(hwnd, SW_NORMAL);
|
||||
if !SetForegroundWindow(hwnd).as_bool() {
|
||||
anyhow::bail!("Failed to set window foreground");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const DELAY_MS: u64 = 25; // Delay between keystrokes
|
||||
const DELAY_MS_STEPS: u64 = 200; // Delay between steps
|
||||
|
||||
fn send_virtual_key(vk: VIRTUAL_KEY, press: bool) -> Result<()> {
|
||||
let mut input = INPUT {
|
||||
r#type: INPUT_KEYBOARD,
|
||||
Anonymous: windows::Win32::UI::Input::KeyboardAndMouse::INPUT_0 {
|
||||
ki: KEYBDINPUT {
|
||||
wVk: vk,
|
||||
wScan: 0,
|
||||
dwFlags: if !press {
|
||||
KEYEVENTF_KEYUP
|
||||
} else {
|
||||
Default::default()
|
||||
},
|
||||
time: 0,
|
||||
dwExtraInfo: 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
unsafe {
|
||||
SendInput(&[input], std::mem::size_of::<INPUT>() as i32);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn send_unicode_char(c: char) -> Result<()> {
|
||||
let mut input = INPUT {
|
||||
r#type: INPUT_KEYBOARD,
|
||||
Anonymous: windows::Win32::UI::Input::KeyboardAndMouse::INPUT_0 {
|
||||
ki: KEYBDINPUT {
|
||||
wVk: VIRTUAL_KEY(0),
|
||||
wScan: c as u16,
|
||||
dwFlags: windows::Win32::UI::Input::KeyboardAndMouse::KEYEVENTF_UNICODE,
|
||||
time: 0,
|
||||
dwExtraInfo: 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
unsafe {
|
||||
SendInput(&[input], std::mem::size_of::<INPUT>() as i32);
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(DELAY_MS));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn perform_autotype(username: &str, password: &str, send_enter: bool) -> Result<()> {
|
||||
// Type username
|
||||
for c in username.chars() {
|
||||
send_unicode_char(c)?;
|
||||
}
|
||||
|
||||
std::thread::sleep(std::time::Duration::from_millis(DELAY_MS_STEPS));
|
||||
|
||||
// Press TAB
|
||||
send_virtual_key(VK_TAB, true)?;
|
||||
send_virtual_key(VK_TAB, false)?;
|
||||
std::thread::sleep(std::time::Duration::from_millis(DELAY_MS));
|
||||
|
||||
std::thread::sleep(std::time::Duration::from_millis(DELAY_MS_STEPS));
|
||||
|
||||
// Type password
|
||||
for c in password.chars() {
|
||||
send_unicode_char(c)?;
|
||||
}
|
||||
|
||||
std::thread::sleep(std::time::Duration::from_millis(DELAY_MS_STEPS));
|
||||
|
||||
// Optionally press Enter
|
||||
if send_enter {
|
||||
send_virtual_key(VK_RETURN, true)?;
|
||||
send_virtual_key(VK_RETURN, false)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_focused_window() -> Result<WindowInfo> {
|
||||
unsafe {
|
||||
let hwnd = GetForegroundWindow();
|
||||
if hwnd.0.is_null() {
|
||||
anyhow::bail!("No foreground window found");
|
||||
}
|
||||
|
||||
let mut buf = [0u16; 512];
|
||||
let len = GetWindowTextW(hwnd, &mut buf);
|
||||
if len == 0 {
|
||||
anyhow::bail!("Failed to get window title");
|
||||
}
|
||||
|
||||
let title = OsString::from_wide(&buf[..len as usize])
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
|
||||
Ok(WindowInfo {
|
||||
handle: hwnd.0 as isize,
|
||||
title,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ tokio-stream = { workspace = true }
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows-registry = { workspace = true }
|
||||
windows_plugin_authenticator = { path = "../windows_plugin_authenticator" }
|
||||
autotype = { path = "../autotype" }
|
||||
|
||||
[build-dependencies]
|
||||
napi-build = { workspace = true }
|
||||
|
||||
10
apps/desktop/desktop_native/napi/index.d.ts
vendored
10
apps/desktop/desktop_native/napi/index.d.ts
vendored
@@ -198,3 +198,13 @@ export declare namespace logging {
|
||||
}
|
||||
export function initNapiLog(jsLogFn: (err: Error | null, arg0: LogLevel, arg1: string) => any): void
|
||||
}
|
||||
export declare namespace autotype {
|
||||
export interface WindowInfo {
|
||||
handle: number
|
||||
title: string
|
||||
}
|
||||
export function getActiveWindows(): Promise<Array<WindowInfo>>
|
||||
export function getFocusedWindow(): Promise<WindowInfo>
|
||||
export function setWindowForeground(handle: number): Promise<void>
|
||||
export function performAutotype(username: string, password: string, sendEnter: boolean): Promise<void>
|
||||
}
|
||||
|
||||
@@ -875,3 +875,57 @@ pub mod logging {
|
||||
fn flush(&self) {}
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub mod autotype {
|
||||
use autotype::WindowInfo as _WindowInfo;
|
||||
|
||||
#[napi(object)]
|
||||
pub struct WindowInfo {
|
||||
pub handle: u32,
|
||||
pub title: String,
|
||||
}
|
||||
|
||||
impl From<_WindowInfo> for WindowInfo {
|
||||
fn from(w: _WindowInfo) -> Self {
|
||||
WindowInfo {
|
||||
handle: w.handle as u32,
|
||||
title: w.title,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub async fn get_active_windows() -> napi::Result<Vec<WindowInfo>> {
|
||||
autotype::get_active_windows()
|
||||
.map(|windows| windows.into_iter().map(WindowInfo::from).collect())
|
||||
.map_err(|e| napi::Error::from_reason(e.to_string()))
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub async fn get_focused_window() -> napi::Result<WindowInfo> {
|
||||
autotype::get_focused_window()
|
||||
.map(WindowInfo::from)
|
||||
.map_err(|e| napi::Error::from_reason(e.to_string()))
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub async fn set_window_foreground(handle: u32) -> napi::Result<()> {
|
||||
autotype::set_window_foreground(handle as isize).map_err(|e| {
|
||||
napi::Error::from_reason(format!(
|
||||
"Setting window foreground failed - Error: {e} - {e:?}"
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub async fn perform_autotype(
|
||||
username: String,
|
||||
password: String,
|
||||
send_enter: bool,
|
||||
) -> napi::Result<()> {
|
||||
autotype::perform_autotype(&username, &password, send_enter).map_err(|e| {
|
||||
napi::Error::from_reason(format!("Perform autotype failed - Error: {e} - {e:?}"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import { MenuMain } from "./main/menu/menu.main";
|
||||
import { MessagingMain } from "./main/messaging.main";
|
||||
import { NativeMessagingMain } from "./main/native-messaging.main";
|
||||
import { PowerMonitorMain } from "./main/power-monitor.main";
|
||||
import { ShortcutsMain } from "./main/shortcuts.main";
|
||||
import { TrayMain } from "./main/tray.main";
|
||||
import { UpdaterMain } from "./main/updater.main";
|
||||
import { WindowMain } from "./main/window.main";
|
||||
@@ -53,6 +54,7 @@ import { ElectronStorageService } from "./platform/services/electron-storage.ser
|
||||
import { EphemeralValueStorageService } from "./platform/services/ephemeral-value-storage.main.service";
|
||||
import { I18nMainService } from "./platform/services/i18n.main.service";
|
||||
import { SSOLocalhostCallbackService } from "./platform/services/sso-localhost-callback.service";
|
||||
import { AutotypeService } from "./services/autotype.service";
|
||||
import { ElectronMainMessagingService } from "./services/electron-main-messaging.service";
|
||||
import { isMacAppStore } from "./utils";
|
||||
|
||||
@@ -75,6 +77,7 @@ export class Main {
|
||||
messagingMain: MessagingMain;
|
||||
updaterMain: UpdaterMain;
|
||||
menuMain: MenuMain;
|
||||
shortcutsMain: ShortcutsMain;
|
||||
powerMonitorMain: PowerMonitorMain;
|
||||
trayMain: TrayMain;
|
||||
biometricsService: DesktopBiometricsService;
|
||||
@@ -105,6 +108,10 @@ export class Main {
|
||||
// on ready stuff...
|
||||
});
|
||||
|
||||
app.on("will-quit", () => {
|
||||
this.shortcutsMain.destroy();
|
||||
});
|
||||
|
||||
if (appDataPath != null) {
|
||||
app.setPath("userData", appDataPath);
|
||||
}
|
||||
@@ -286,6 +293,8 @@ export class Main {
|
||||
this.ssoUrlService,
|
||||
);
|
||||
|
||||
this.shortcutsMain = new ShortcutsMain(this, new AutotypeService());
|
||||
|
||||
this.nativeAutofillMain = new NativeAutofillMain(this.logService, this.windowMain);
|
||||
void this.nativeAutofillMain.init();
|
||||
}
|
||||
@@ -318,6 +327,7 @@ export class Main {
|
||||
}
|
||||
this.powerMonitorMain.init();
|
||||
await this.updaterMain.init();
|
||||
await this.shortcutsMain.init();
|
||||
|
||||
const [browserIntegrationEnabled, ddgIntegrationEnabled] = await Promise.all([
|
||||
firstValueFrom(this.desktopSettingsService.browserIntegrationEnabled$),
|
||||
|
||||
53
apps/desktop/src/main/shortcuts.main.ts
Normal file
53
apps/desktop/src/main/shortcuts.main.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import { globalShortcut } from "electron";
|
||||
|
||||
import { Main } from "../main";
|
||||
import { AutotypeService } from "../services/autotype.service";
|
||||
|
||||
export class ShortcutsMain {
|
||||
constructor(
|
||||
private main: Main,
|
||||
private autotypeService: AutotypeService,
|
||||
) {}
|
||||
|
||||
async init() {
|
||||
globalShortcut.register("CommandOrControl+Shift+L", async () => {
|
||||
console.log("Autofill shortcut triggered!");
|
||||
const focusedWindow = await this.autotypeService.getFocusedWindow();
|
||||
console.log(focusedWindow);
|
||||
// TODO: Look up cipher with focusedWindow.title
|
||||
const shouldAutofill = focusedWindow.title.indexOf("Notepad") > -1;
|
||||
if (shouldAutofill) {
|
||||
await this.autotypeService.performAutotype("testuser", "testpassword", true);
|
||||
}
|
||||
});
|
||||
|
||||
globalShortcut.register("CommandOrControl+Shift+J", async () => {
|
||||
console.log("Autofill to window shortcut triggered!");
|
||||
|
||||
const currentWindows = await this.autotypeService.getActiveWindows();
|
||||
let windowId: number = null;
|
||||
for (const w of currentWindows) {
|
||||
console.log(`Window ID: ${w.handle}, Title: ${w.title}`);
|
||||
if (w.title.indexOf("Notepad") > -1) {
|
||||
windowId = w.handle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (windowId != null) {
|
||||
await this.autotypeService.setWindowForeground(windowId);
|
||||
}
|
||||
|
||||
const focusedWindow = await this.autotypeService.getFocusedWindow();
|
||||
if (focusedWindow.handle === windowId) {
|
||||
await this.autotypeService.performAutotype("testuser", "testpassword", true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
globalShortcut.unregisterAll();
|
||||
}
|
||||
}
|
||||
39
apps/desktop/src/services/autotype.service.ts
Normal file
39
apps/desktop/src/services/autotype.service.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { ipcMain } from "electron";
|
||||
|
||||
import { autotype } from "@bitwarden/desktop-napi";
|
||||
|
||||
export class AutotypeService {
|
||||
constructor() {
|
||||
ipcMain.handle("autotype.getActiveWindows", async (event) => {
|
||||
return await this.getActiveWindows();
|
||||
});
|
||||
ipcMain.handle("autotype.getFocusedWindow", async (event) => {
|
||||
return await this.getFocusedWindow();
|
||||
});
|
||||
ipcMain.handle("autotype.setWindowForeground", async (event, handle: number) => {
|
||||
return await this.setWindowForeground(handle);
|
||||
});
|
||||
ipcMain.handle(
|
||||
"autotype.performAutotype",
|
||||
async (event, username: string, password: string, sendEnter: boolean) => {
|
||||
return await this.performAutotype(username, password, sendEnter);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async getActiveWindows() {
|
||||
return await autotype.getActiveWindows();
|
||||
}
|
||||
|
||||
async getFocusedWindow() {
|
||||
return await autotype.getFocusedWindow();
|
||||
}
|
||||
|
||||
async setWindowForeground(handle: number) {
|
||||
return await autotype.setWindowForeground(handle);
|
||||
}
|
||||
|
||||
async performAutotype(username: string, password: string, sendEnter: boolean) {
|
||||
return await autotype.performAutotype(username, password, sendEnter);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user