using ElectronNET.API.Entities; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; using System; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using ElectronNET.API.Extensions; using static System.Collections.Specialized.BitVector32; namespace ElectronNET.API { /// /// Control your application's event lifecycle. /// public sealed class App { /// /// Emitted when all windows have been closed. /// /// If you do not subscribe to this event and all windows are closed, the default behavior is to quit /// the app; however, if you subscribe, you control whether the app quits or not.If the user pressed /// Cmd + Q, or the developer called , Electron will first try to close all the windows /// and then emit the event, and in this case the event /// would not be emitted. /// public event Action WindowAllClosed { add { if (_windowAllClosed == null) { BridgeConnector.Socket.On("app-window-all-closed" + GetHashCode(), () => { if (!Electron.WindowManager.IsQuitOnWindowAllClosed || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { _windowAllClosed(); } }); BridgeConnector.Socket.Emit("register-app-window-all-closed-event", GetHashCode()); } _windowAllClosed += value; } remove { _windowAllClosed -= value; if(_windowAllClosed == null) BridgeConnector.Socket.Off("app-window-all-closed" + GetHashCode()); } } private event Action _windowAllClosed; /// /// Emitted before the application starts closing its windows. /// /// Note: If application quit was initiated by then /// is emitted after emitting close event on all windows and closing them. /// /// Note: On Windows, this event will not be emitted if the app is closed due to a shutdown/restart of the system or a user logout. /// public event Func BeforeQuit { add { if (_beforeQuit == null) { BridgeConnector.Socket.On("app-before-quit" + GetHashCode(), async () => { await _beforeQuit(new QuitEventArgs()); if (_preventQuit) { _preventQuit = false; } else { if (_willQuit == null && _quitting == null) { Exit(); } else if (_willQuit != null) { await _willQuit(new QuitEventArgs()); if (_preventQuit) { _preventQuit = false; } else { if (_quitting == null) { Exit(); } else { await _quitting(); Exit(); } } } else if (_quitting != null) { await _quitting(); Exit(); } } }); BridgeConnector.Socket.Emit("register-app-before-quit-event", GetHashCode()); } _beforeQuit += value; } remove { _beforeQuit -= value; if (_beforeQuit == null) BridgeConnector.Socket.Off("app-before-quit" + GetHashCode()); } } private event Func _beforeQuit; /// /// Emitted when all windows have been closed and the application will quit. /// /// See the description of the event for the differences between the /// and events. /// /// Note: On Windows, this event will not be emitted if the app is closed due to a shutdown/restart of the system or a user logout. /// public event Func WillQuit { add { if (_willQuit == null) { BridgeConnector.Socket.On("app-will-quit" + GetHashCode(), async () => { await _willQuit(new QuitEventArgs()); if (_preventQuit) { _preventQuit = false; } else { if (_quitting == null) { Exit(); } else { await _quitting(); Exit(); } } }); BridgeConnector.Socket.Emit("register-app-will-quit-event", GetHashCode()); } _willQuit += value; } remove { _willQuit -= value; if (_willQuit == null) BridgeConnector.Socket.Off("app-will-quit" + GetHashCode()); } } private event Func _willQuit; /// /// Emitted when the application is quitting. /// /// Note: On Windows, this event will not be emitted if the app is closed due to a shutdown/restart of the system or a user logout. /// public event Func Quitting { add { if (_quitting == null) { BridgeConnector.Socket.On("app-will-quit" + GetHashCode() + "quitting", async () => { if(_willQuit == null) { await _quitting(); Exit(); } }); BridgeConnector.Socket.Emit("register-app-will-quit-event", GetHashCode() + "quitting"); } _quitting += value; } remove { _quitting -= value; if (_quitting == null) BridgeConnector.Socket.Off("app-will-quit" + GetHashCode() + "quitting"); } } private event Func _quitting; /// /// Emitted when a blurred. /// public event Action BrowserWindowBlur { add { if (_browserWindowBlur == null) { BridgeConnector.Socket.On("app-browser-window-blur" + GetHashCode(), () => { _browserWindowBlur(); }); BridgeConnector.Socket.Emit("register-app-browser-window-blur-event", GetHashCode()); } _browserWindowBlur += value; } remove { _browserWindowBlur -= value; if (_browserWindowBlur == null) BridgeConnector.Socket.Off("app-browser-window-blur" + GetHashCode()); } } private event Action _browserWindowBlur; /// /// Emitted when a gets focused. /// public event Action BrowserWindowFocus { add { if (_browserWindowFocus == null) { BridgeConnector.Socket.On("app-browser-window-focus" + GetHashCode(), () => { _browserWindowFocus(); }); BridgeConnector.Socket.Emit("register-app-browser-window-focus-event", GetHashCode()); } _browserWindowFocus += value; } remove { _browserWindowFocus -= value; if (_browserWindowFocus == null) BridgeConnector.Socket.Off("app-browser-window-focus" + GetHashCode()); } } private event Action _browserWindowFocus; /// /// Emitted when a new is created. /// public event Action BrowserWindowCreated { add { if (_browserWindowCreated == null) { BridgeConnector.Socket.On("app-browser-window-created" + GetHashCode(), () => { _browserWindowCreated(); }); BridgeConnector.Socket.Emit("register-app-browser-window-created-event", GetHashCode()); } _browserWindowCreated += value; } remove { _browserWindowCreated -= value; if (_browserWindowCreated == null) BridgeConnector.Socket.Off("app-browser-window-created" + GetHashCode()); } } private event Action _browserWindowCreated; /// /// Emitted when a new is created. /// public event Action WebContentsCreated { add { if (_webContentsCreated == null) { BridgeConnector.Socket.On("app-web-contents-created" + GetHashCode(), () => { _webContentsCreated(); }); BridgeConnector.Socket.Emit("register-app-web-contents-created-event", GetHashCode()); } _webContentsCreated += value; } remove { _webContentsCreated -= value; if (_webContentsCreated == null) BridgeConnector.Socket.Off("app-web-contents-created" + GetHashCode()); } } private event Action _webContentsCreated; /// /// Emitted when Chrome’s accessibility support changes. This event fires when assistive technologies, such as /// screen readers, are enabled or disabled. See https://www.chromium.org/developers/design-documents/accessibility for more details. /// /// when Chrome's accessibility support is enabled, otherwise. public event Action AccessibilitySupportChanged { add { if (_accessibilitySupportChanged == null) { BridgeConnector.Socket.On("app-accessibility-support-changed" + GetHashCode(), (state) => { _accessibilitySupportChanged((bool)state); }); BridgeConnector.Socket.Emit("register-app-accessibility-support-changed-event", GetHashCode()); } _accessibilitySupportChanged += value; } remove { _accessibilitySupportChanged -= value; if (_accessibilitySupportChanged == null) BridgeConnector.Socket.Off("app-accessibility-support-changed" + GetHashCode()); } } private event Action _accessibilitySupportChanged; /// /// Emitted when the application has finished basic startup. /// public event Action Ready { add { if(IsReady) { value(); } _ready += value; } remove { _ready -= value; } } private event Action _ready; /// /// Application host fully started. /// public bool IsReady { get { return _isReady; } internal set { _isReady = value; if(value) { _ready?.Invoke(); } } } private bool _isReady = false; /// /// Emitted when a MacOS user wants to open a file with the application. The open-file event is usually emitted /// when the application is already open and the OS wants to reuse the application to open the file. /// open-file is also emitted when a file is dropped onto the dock and the application is not yet running. /// /// On Windows, you have to parse the arguments using App.CommandLine to get the filepath. /// public event Action OpenFile { add { if (_openFile == null) { BridgeConnector.Socket.On("app-open-file" + GetHashCode(), (file) => { _openFile(file.ToString()); }); BridgeConnector.Socket.Emit("register-app-open-file-event", GetHashCode()); } _openFile += value; } remove { _openFile -= value; if (_openFile == null) BridgeConnector.Socket.Off("app-open-file" + GetHashCode()); } } private event Action _openFile; /// /// Emitted when a MacOS user wants to open a URL with the application. Your application's Info.plist file must /// define the URL scheme within the CFBundleURLTypes key, and set NSPrincipalClass to AtomApplication. /// public event Action OpenUrl { add { if (_openUrl == null) { BridgeConnector.Socket.On("app-open-url" + GetHashCode(), (url) => { _openUrl(url.ToString()); }); BridgeConnector.Socket.Emit("register-app-open-url-event", GetHashCode()); } _openUrl += value; } remove { _openUrl -= value; if (_openUrl == null) BridgeConnector.Socket.Off("app-open-url" + GetHashCode()); } } private event Action _openUrl; /// /// A property that indicates the current application's name, which is the name in the /// application's package.json file. /// /// Usually the name field of package.json is a short lowercase name, according to the npm modules spec. You /// should usually also specify a productName field, which is your application's full capitalized name, and /// which will be preferred over name by Electron. /// public string Name { [Obsolete("Use the asynchronous version NameAsync instead")] get { return NameAsync.Result; } set { BridgeConnector.Socket.Emit("appSetName", value); } } /// /// A property that indicates the current application's name, which is the name in the /// application's package.json file. /// /// Usually the name field of package.json is a short lowercase name, according to the npm modules spec. You /// should usually also specify a productName field, which is your application's full capitalized name, and /// which will be preferred over name by Electron. /// public Task NameAsync { get { return Task.Run(() => { var taskCompletionSource = new TaskCompletionSource(); BridgeConnector.Socket.On("appGetNameCompleted", (result) => { BridgeConnector.Socket.Off("appGetNameCompleted"); taskCompletionSource.SetResult((string)result); }); BridgeConnector.Socket.Emit("appGetName"); return taskCompletionSource.Task; }); } } internal App() { CommandLine = new CommandLine(); } internal static App Instance { get { if (_app == null) { lock (_syncRoot) { if(_app == null) { _app = new App(); } } } return _app; } } private static App _app; private static object _syncRoot = new object(); private readonly JsonSerializer _jsonSerializer = new JsonSerializer() { ContractResolver = new CamelCasePropertyNamesContractResolver() }; /// /// Try to close all windows. The event will be emitted first. If all windows are successfully /// closed, the event will be emitted and by default the application will terminate. This method /// guarantees that all beforeunload and unload event handlers are correctly executed. It is possible /// that a window cancels the quitting by returning in the beforeunload event handler. /// public void Quit() { BridgeConnector.Socket.Emit("appQuit"); } /// /// All windows will be closed immediately without asking user and the and /// events will not be emitted. /// /// Exits immediately with exitCode. exitCode defaults to 0. public void Exit(int exitCode = 0) { BridgeConnector.Socket.Emit("appExit", exitCode); } /// /// Relaunches the app when current instance exits. By default the new instance will use the same working directory /// and command line arguments with current instance. /// /// Note that this method does not quit the app when executed, you have to call or /// after calling to make the app restart. /// /// When is called for multiple times, multiple instances will be started after current instance /// exited. /// public void Relaunch() { BridgeConnector.Socket.Emit("appRelaunch"); } /// /// Relaunches the app when current instance exits. By default the new instance will use the same working directory /// and command line arguments with current instance. When is specified, the /// will be passed as command line arguments instead. When /// is specified, the will be executed for relaunch instead of current app. /// /// Note that this method does not quit the app when executed, you have to call or /// after calling to make the app restart. /// /// When is called for multiple times, multiple instances will be started after current instance /// exited. /// /// Options for the relaunch. public void Relaunch(RelaunchOptions relaunchOptions) { BridgeConnector.Socket.Emit("appRelaunch", JObject.FromObject(relaunchOptions, _jsonSerializer)); } /// /// On Linux, focuses on the first visible window. On macOS, makes the application the active app. On Windows, focuses /// on the application's first window. /// public void Focus() { BridgeConnector.Socket.Emit("appFocus"); } /// /// On Linux, focuses on the first visible window. On macOS, makes the application the active app. On Windows, focuses /// on the application's first window. /// /// You should seek to use the option as sparingly as possible. /// public void Focus(FocusOptions focusOptions) { BridgeConnector.Socket.Emit("appFocus", JObject.FromObject(focusOptions, _jsonSerializer)); } /// /// Hides all application windows without minimizing them. /// public void Hide() { BridgeConnector.Socket.Emit("appHide"); } /// /// Shows application windows after they were hidden. Does not automatically focus them. /// public void Show() { BridgeConnector.Socket.Emit("appShow"); } /// /// The current application directory. /// public async Task GetAppPathAsync(CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var taskCompletionSource = new TaskCompletionSource(); using(cancellationToken.Register(() => taskCompletionSource.TrySetCanceled())) { BridgeConnector.Socket.On("appGetAppPathCompleted", (path) => { BridgeConnector.Socket.Off("appGetAppPathCompleted"); taskCompletionSource.SetResult(path.ToString()); }); BridgeConnector.Socket.Emit("appGetAppPath"); return await taskCompletionSource.Task .ConfigureAwait(false); } } /// /// Sets or creates a directory your app's logs which can then be manipulated with /// or . /// /// Calling without a path parameter will result in this directory being set to /// ~/Library/Logs/YourAppName on macOS, and inside the userData directory on Linux and Windows. /// /// A custom path for your logs. Must be absolute. public void SetAppLogsPath(string path) { BridgeConnector.Socket.Emit("appSetAppLogsPath", path); } /// /// The path to a special directory. If is called without called /// being called first, a default directory will be created equivalent /// to calling without a path parameter. /// /// Special directory. /// The cancellation token. /// A path to a special directory or file associated with name. public async Task GetPathAsync(PathName pathName, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var taskCompletionSource = new TaskCompletionSource(); using(cancellationToken.Register(() => taskCompletionSource.TrySetCanceled())) { BridgeConnector.Socket.On("appGetPathCompleted", (path) => { BridgeConnector.Socket.Off("appGetPathCompleted"); taskCompletionSource.SetResult(path.ToString()); }); BridgeConnector.Socket.Emit("appGetPath", pathName.GetDescription()); return await taskCompletionSource.Task .ConfigureAwait(false); } } /// /// Overrides the path to a special directory or file associated with name. If the path specifies a directory /// that does not exist, an Error is thrown. In that case, the directory should be created with fs.mkdirSync or similar. /// /// You can only override paths of a name defined in . /// /// By default, web pages' cookies and caches will be stored under the directory. If you /// want to change this location, you have to override the path before the /// event of the module is emitted. /// Special directory. /// New path to a special directory. /// public void SetPath(PathName name, string path) { BridgeConnector.Socket.Emit("appSetPath", name.GetDescription(), path); } /// /// The version of the loaded application. If no version is found in the application’s package.json file, /// the version of the current bundle or executable is returned. /// /// The version of the loaded application. public async Task GetVersionAsync(CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var taskCompletionSource = new TaskCompletionSource(); using(cancellationToken.Register(() => taskCompletionSource.TrySetCanceled())) { BridgeConnector.Socket.On("appGetVersionCompleted", (version) => { BridgeConnector.Socket.Off("appGetVersionCompleted"); taskCompletionSource.SetResult(version.ToString()); }); BridgeConnector.Socket.Emit("appGetVersion"); return await taskCompletionSource.Task .ConfigureAwait(false); } } /// /// The current application locale. Possible return values are documented here. /// /// Note: When distributing your packaged app, you have to also ship the locales folder. /// /// Note: On Windows, you have to call it after the events gets emitted. /// /// The current application locale. public async Task GetLocaleAsync(CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var taskCompletionSource = new TaskCompletionSource(); using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled())) { BridgeConnector.Socket.On("appGetLocaleCompleted", (local) => { BridgeConnector.Socket.Off("appGetLocaleCompleted"); taskCompletionSource.SetResult(local.ToString()); }); BridgeConnector.Socket.Emit("appGetLocale"); return await taskCompletionSource.Task .ConfigureAwait(false); } } /// /// Adds path to the recent documents list. This list is managed by the OS. On Windows you can visit the /// list from the task bar, and on macOS you can visit it from dock menu. /// /// Path to add. public void AddRecentDocument(string path) { BridgeConnector.Socket.Emit("appAddRecentDocument", path); } /// /// Clears the recent documents list. /// public void ClearRecentDocuments() { BridgeConnector.Socket.Emit("appClearRecentDocuments"); } /// /// Sets the current executable as the default handler for a protocol (aka URI scheme). It allows you to /// integrate your app deeper into the operating system. Once registered, all links with your-protocol:// /// will be opened with the current executable. The whole link, including protocol, will be passed to your /// application as a parameter. /// /// Note: On macOS, you can only register protocols that have been added to your app's info.plist, which /// cannot be modified at runtime. However, you can change the file during build time via /// Electron Forge, /// Electron Packager, or by editing info.plist /// with a text editor. Please refer to /// Apple's documentation /// for details. /// /// Note: In a Windows Store environment (when packaged as an appx) this API will return true for all calls but /// the registry key it sets won't be accessible by other applications. In order to register your Windows Store /// application as a default protocol handler you must declare the protocol in your manifest. /// /// The API uses the Windows Registry and LSSetDefaultHandlerForURLScheme internally. /// /// /// The name of your protocol, without ://. For example, if you want your app to handle electron:// links, /// call this method with electron as the parameter. /// The cancellation token. /// Whether the call succeeded. public async Task SetAsDefaultProtocolClientAsync(string protocol, CancellationToken cancellationToken = default) { return await SetAsDefaultProtocolClientAsync(protocol, null, null, cancellationToken); } /// /// Sets the current executable as the default handler for a protocol (aka URI scheme). It allows you to /// integrate your app deeper into the operating system. Once registered, all links with your-protocol:// /// will be opened with the current executable. The whole link, including protocol, will be passed to your /// application as a parameter. /// /// Note: On macOS, you can only register protocols that have been added to your app's info.plist, which /// cannot be modified at runtime. However, you can change the file during build time via /// Electron Forge, /// Electron Packager, or by editing info.plist /// with a text editor. Please refer to /// Apple's documentation /// for details. /// /// Note: In a Windows Store environment (when packaged as an appx) this API will return true for all calls but /// the registry key it sets won't be accessible by other applications. In order to register your Windows Store /// application as a default protocol handler you must declare the protocol in your manifest. /// /// The API uses the Windows Registry and LSSetDefaultHandlerForURLScheme internally. /// /// /// The name of your protocol, without ://. For example, if you want your app to handle electron:// links, /// call this method with electron as the parameter. /// The path to the Electron executable. Defaults to process.execPath /// The cancellation token. /// Whether the call succeeded. public async Task SetAsDefaultProtocolClientAsync(string protocol, string path, CancellationToken cancellationToken = default) { return await SetAsDefaultProtocolClientAsync(protocol, path, null, cancellationToken); } /// /// Sets the current executable as the default handler for a protocol (aka URI scheme). It allows you to /// integrate your app deeper into the operating system. Once registered, all links with your-protocol:// /// will be opened with the current executable. The whole link, including protocol, will be passed to your /// application as a parameter. /// /// Note: On macOS, you can only register protocols that have been added to your app's info.plist, which /// cannot be modified at runtime. However, you can change the file during build time via /// Electron Forge, /// Electron Packager, or by editing info.plist /// with a text editor. Please refer to /// Apple's documentation /// for details. /// /// Note: In a Windows Store environment (when packaged as an appx) this API will return true for all calls but /// the registry key it sets won't be accessible by other applications. In order to register your Windows Store /// application as a default protocol handler you must declare the protocol in your manifest. /// /// The API uses the Windows Registry and LSSetDefaultHandlerForURLScheme internally. /// /// /// The name of your protocol, without ://. For example, if you want your app to handle electron:// links, /// call this method with electron as the parameter. /// The path to the Electron executable. Defaults to process.execPath /// Arguments passed to the executable. Defaults to an empty array. /// The cancellation token. /// Whether the call succeeded. public async Task SetAsDefaultProtocolClientAsync(string protocol, string path, string[] args, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var taskCompletionSource = new TaskCompletionSource(); using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled())) { BridgeConnector.Socket.On("appSetAsDefaultProtocolClientCompleted", (success) => { BridgeConnector.Socket.Off("appSetAsDefaultProtocolClientCompleted"); taskCompletionSource.SetResult((bool) success); }); BridgeConnector.Socket.Emit("appSetAsDefaultProtocolClient", protocol, path, args); return await taskCompletionSource.Task .ConfigureAwait(false); } } /// /// This method checks if the current executable as the default handler for a protocol (aka URI scheme). /// If so, it will remove the app as the default handler. /// /// The name of your protocol, without ://. /// The cancellation token. /// Whether the call succeeded. public async Task RemoveAsDefaultProtocolClientAsync(string protocol, CancellationToken cancellationToken = default) { return await RemoveAsDefaultProtocolClientAsync(protocol, null, null, cancellationToken); } /// /// This method checks if the current executable as the default handler for a protocol (aka URI scheme). /// If so, it will remove the app as the default handler. /// /// The name of your protocol, without ://. /// Defaults to process.execPath. /// The cancellation token. /// Whether the call succeeded. public async Task RemoveAsDefaultProtocolClientAsync(string protocol, string path, CancellationToken cancellationToken = default) { return await RemoveAsDefaultProtocolClientAsync(protocol, path, null, cancellationToken); } /// /// This method checks if the current executable as the default handler for a protocol (aka URI scheme). /// If so, it will remove the app as the default handler. /// /// The name of your protocol, without ://. /// Defaults to process.execPath. /// Defaults to an empty array. /// The cancellation token. /// Whether the call succeeded. public async Task RemoveAsDefaultProtocolClientAsync(string protocol, string path, string[] args, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var taskCompletionSource = new TaskCompletionSource(); using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled())) { BridgeConnector.Socket.On("appRemoveAsDefaultProtocolClientCompleted", (success) => { BridgeConnector.Socket.Off("appRemoveAsDefaultProtocolClientCompleted"); taskCompletionSource.SetResult((bool) success); }); BridgeConnector.Socket.Emit("appRemoveAsDefaultProtocolClient", protocol, path, args); return await taskCompletionSource.Task .ConfigureAwait(false); } } /// /// This method checks if the current executable is the default handler for a protocol (aka URI scheme). /// /// Note: On macOS, you can use this method to check if the app has been registered as the default protocol /// handler for a protocol. You can also verify this by checking ~/Library/Preferences/com.apple.LaunchServices.plist /// on the macOS machine. Please refer to Apple's documentation /// for details. /// /// The API uses the Windows Registry and LSCopyDefaultHandlerForURLScheme internally. /// /// The name of your protocol, without ://. /// The cancellation token. /// Whether the current executable is the default handler for a protocol (aka URI scheme). public async Task IsDefaultProtocolClientAsync(string protocol, CancellationToken cancellationToken = default) { return await IsDefaultProtocolClientAsync(protocol, null, null, cancellationToken); } /// /// This method checks if the current executable is the default handler for a protocol (aka URI scheme). /// /// Note: On macOS, you can use this method to check if the app has been registered as the default protocol /// handler for a protocol. You can also verify this by checking ~/Library/Preferences/com.apple.LaunchServices.plist /// on the macOS machine. Please refer to Apple's documentation /// for details. /// /// The API uses the Windows Registry and LSCopyDefaultHandlerForURLScheme internally. /// /// The name of your protocol, without ://. /// Defaults to process.execPath. /// The cancellation token. /// Whether the current executable is the default handler for a protocol (aka URI scheme). public async Task IsDefaultProtocolClientAsync(string protocol, string path, CancellationToken cancellationToken = default) { return await IsDefaultProtocolClientAsync(protocol, path, null, cancellationToken); } /// /// This method checks if the current executable is the default handler for a protocol (aka URI scheme). /// /// Note: On macOS, you can use this method to check if the app has been registered as the default protocol /// handler for a protocol. You can also verify this by checking ~/Library/Preferences/com.apple.LaunchServices.plist /// on the macOS machine. Please refer to Apple's documentation /// for details. /// /// The API uses the Windows Registry and LSCopyDefaultHandlerForURLScheme internally. /// /// The name of your protocol, without ://. /// Defaults to process.execPath. /// Defaults to an empty array. /// The cancellation token. /// Whether the current executable is the default handler for a protocol (aka URI scheme). public async Task IsDefaultProtocolClientAsync(string protocol, string path, string[] args, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var taskCompletionSource = new TaskCompletionSource(); using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled())) { BridgeConnector.Socket.On("appIsDefaultProtocolClientCompleted", (success) => { BridgeConnector.Socket.Off("appIsDefaultProtocolClientCompleted"); taskCompletionSource.SetResult((bool) success); }); BridgeConnector.Socket.Emit("appIsDefaultProtocolClient", protocol, path, args); return await taskCompletionSource.Task .ConfigureAwait(false); } } /// /// Adds tasks to the category of the JumpList on Windows. /// /// Note: If you'd like to customize the Jump List even more use instead. /// /// Array of objects. /// The cancellation token. /// Whether the call succeeded. public async Task SetUserTasksAsync(UserTask[] userTasks, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var taskCompletionSource = new TaskCompletionSource(); using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled())) { BridgeConnector.Socket.On("appSetUserTasksCompleted", (success) => { BridgeConnector.Socket.Off("appSetUserTasksCompleted"); taskCompletionSource.SetResult((bool) success); }); BridgeConnector.Socket.Emit("appSetUserTasks", JArray.FromObject(userTasks, _jsonSerializer)); return await taskCompletionSource.Task .ConfigureAwait(false); } } /// /// Jump List settings for the application. /// /// The cancellation token. /// Jump List settings. public async Task GetJumpListSettingsAsync(CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var taskCompletionSource = new TaskCompletionSource(); using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled())) { BridgeConnector.Socket.On("appGetJumpListSettingsCompleted", (jumpListSettings) => { BridgeConnector.Socket.Off("appGetJumpListSettingsCompleted"); taskCompletionSource.SetResult(JObject.Parse(jumpListSettings.ToString()).ToObject()); }); BridgeConnector.Socket.Emit("appGetJumpListSettings"); return await taskCompletionSource.Task .ConfigureAwait(false); } } /// /// Sets or removes a custom Jump List for the application. If categories is null the previously set custom /// Jump List (if any) will be replaced by the standard Jump List for the app (managed by Windows). /// /// Note: If a object has neither the nor /// the property set then its is assumed /// to be . If the property is set but /// the property is omitted then the is /// assumed to be . /// /// Note: Users can remove items from custom categories, and Windows will not allow a removed item to be added /// back into a custom category until after the next successful call to . Any attempt /// to re-add a removed item to a custom category earlier than that will result in the entire custom category being /// omitted from the Jump List. The list of removed items can be obtained using . /// /// Array of objects. public void SetJumpList(JumpListCategory[] categories) { BridgeConnector.Socket.Emit("appSetJumpList", JArray.FromObject(categories, _jsonSerializer)); } /// /// The return value of this method indicates whether or not this instance of your application successfully obtained /// the lock. If it failed to obtain the lock, you can assume that another instance of your application is already /// running with the lock and exit immediately. /// /// I.e.This method returns if your process is the primary instance of your application and your /// app should continue loading. It returns if your process should immediately quit as it has /// sent its parameters to another instance that has already acquired the lock. /// /// On macOS, the system enforces single instance automatically when users try to open a second instance of your app /// in Finder, and the open-file and open-url events will be emitted for that.However when users start your app in /// command line, the system's single instance mechanism will be bypassed, and you have to use this method to ensure /// single instance. /// /// Lambda with an array of the second instance’s command line arguments. /// The second parameter is the working directory path. /// The cancellation token. /// This method returns false if your process is the primary instance of the application and your app /// should continue loading. And returns true if your process has sent its parameters to another instance, and /// you should immediately quit. /// public async Task RequestSingleInstanceLockAsync(Action newInstanceOpened, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var taskCompletionSource = new TaskCompletionSource(); using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled())) { BridgeConnector.Socket.On("appRequestSingleInstanceLockCompleted", (success) => { BridgeConnector.Socket.Off("appRequestSingleInstanceLockCompleted"); taskCompletionSource.SetResult((bool)success); }); BridgeConnector.Socket.Off("secondInstance"); BridgeConnector.Socket.On("secondInstance", (result) => { JArray results = (JArray)result; string[] args = results.First.ToObject(); string workingDirectory = results.Last.ToObject(); newInstanceOpened(args, workingDirectory); }); BridgeConnector.Socket.Emit("appRequestSingleInstanceLock"); return await taskCompletionSource.Task .ConfigureAwait(false); } } /// /// Releases all locks that were created by makeSingleInstance. This will allow /// multiple instances of the application to once again run side by side. /// public void ReleaseSingleInstanceLock() { BridgeConnector.Socket.Emit("appReleaseSingleInstanceLock"); } /// /// This method returns whether or not this instance of your app is currently holding the single instance lock. /// You can request the lock with and release with /// . /// /// The cancellation token. public async Task HasSingleInstanceLockAsync(CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var taskCompletionSource = new TaskCompletionSource(); using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled())) { BridgeConnector.Socket.On("appHasSingleInstanceLockCompleted", (hasLock) => { BridgeConnector.Socket.Off("appHasSingleInstanceLockCompleted"); taskCompletionSource.SetResult((bool) hasLock); }); BridgeConnector.Socket.Emit("appHasSingleInstanceLock"); return await taskCompletionSource.Task .ConfigureAwait(false); } } /// /// Creates an NSUserActivity and sets it as the current activity. The activity is /// eligible for Handoff /// to another device afterward. /// /// Uniquely identifies the activity. Maps to NSUserActivity.activityType. /// App-specific state to store for use by another device. public void SetUserActivity(string type, object userInfo) { SetUserActivity(type, userInfo, null); } /// /// Creates an NSUserActivity and sets it as the current activity. The activity is /// eligible for Handoff /// to another device afterward. /// /// /// Uniquely identifies the activity. Maps to NSUserActivity.activityType. /// /// App-specific state to store for use by another device. /// /// The webpage to load in a browser if no suitable app is installed on the resuming device. The scheme must be http or https. /// public void SetUserActivity(string type, object userInfo, string webpageUrl) { BridgeConnector.Socket.Emit("appSetUserActivity", type, userInfo, webpageUrl); } /// /// The type of the currently running activity. /// /// The cancellation token. public async Task GetCurrentActivityTypeAsync(CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var taskCompletionSource = new TaskCompletionSource(); using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled())) { BridgeConnector.Socket.On("appGetCurrentActivityTypeCompleted", (activityType) => { BridgeConnector.Socket.Off("appGetCurrentActivityTypeCompleted"); taskCompletionSource.SetResult(activityType.ToString()); }); BridgeConnector.Socket.Emit("appGetCurrentActivityType"); return await taskCompletionSource.Task .ConfigureAwait(false); } } /// /// Invalidates the current Handoff user activity. /// public void InvalidateCurrentActivity() { BridgeConnector.Socket.Emit("appInvalidateCurrentActivity"); } /// /// Marks the current Handoff user activity as inactive without invalidating it. /// public void ResignCurrentActivity() { BridgeConnector.Socket.Emit("appResignCurrentActivity"); } /// /// Changes the Application User Model ID to id. /// /// Model Id. public void SetAppUserModelId(string id) { BridgeConnector.Socket.Emit("appSetAppUserModelId", id); } /// TODO: Check new parameter which is a function [App.ImportCertificate] /// /// Imports the certificate in pkcs12 format into the platform certificate store. /// callback is called with the result of import operation, a value of 0 indicates /// success while any other value indicates failure according to chromium net_error_list. /// /// /// The cancellation token. /// Result of import. Value of 0 indicates success. public async Task ImportCertificateAsync(ImportCertificateOptions options, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var taskCompletionSource = new TaskCompletionSource(); using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled())) { BridgeConnector.Socket.On("appImportCertificateCompleted", (result) => { BridgeConnector.Socket.Off("appImportCertificateCompleted"); taskCompletionSource.SetResult((int) result); }); BridgeConnector.Socket.Emit("appImportCertificate", JObject.FromObject(options, _jsonSerializer)); return await taskCompletionSource.Task .ConfigureAwait(false); } } /// /// Memory and cpu usage statistics of all the processes associated with the app. /// /// /// Array of ProcessMetric objects that correspond to memory and cpu usage /// statistics of all the processes associated with the app. /// The cancellation token. /// public async Task GetAppMetricsAsync(CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var taskCompletionSource = new TaskCompletionSource(); using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled())) { BridgeConnector.Socket.On("appGetAppMetricsCompleted", (result) => { BridgeConnector.Socket.Off("appGetAppMetricsCompleted"); var processMetrics = ((JArray)result).ToObject(); taskCompletionSource.SetResult(processMetrics); }); BridgeConnector.Socket.Emit("appGetAppMetrics"); return await taskCompletionSource.Task .ConfigureAwait(false); } } /// /// The Graphics Feature Status from chrome://gpu/. /// /// Note: This information is only usable after the gpu-info-update event is emitted. /// The cancellation token. /// public async Task GetGpuFeatureStatusAsync(CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var taskCompletionSource = new TaskCompletionSource(); using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled())) { BridgeConnector.Socket.On("appGetGpuFeatureStatusCompleted", (result) => { BridgeConnector.Socket.Off("appGetGpuFeatureStatusCompleted"); var gpuFeatureStatus = ((JObject)result).ToObject(); taskCompletionSource.SetResult(gpuFeatureStatus); }); BridgeConnector.Socket.Emit("appGetGpuFeatureStatus"); return await taskCompletionSource.Task .ConfigureAwait(false); } } /// /// Sets the counter badge for current app. Setting the count to 0 will hide the badge. /// On macOS it shows on the dock icon. On Linux it only works for Unity launcher. /// /// Note: Unity launcher requires the existence of a .desktop file to work, for more /// information please read Desktop Environment Integration. /// /// Counter badge. /// The cancellation token. /// Whether the call succeeded. public async Task SetBadgeCountAsync(int count, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var taskCompletionSource = new TaskCompletionSource(); using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled())) { BridgeConnector.Socket.On("appSetBadgeCountCompleted", (success) => { BridgeConnector.Socket.Off("appSetBadgeCountCompleted"); taskCompletionSource.SetResult((bool) success); }); BridgeConnector.Socket.Emit("appSetBadgeCount", count); return await taskCompletionSource.Task .ConfigureAwait(false); } } /// /// The current value displayed in the counter badge. /// /// The cancellation token. public async Task GetBadgeCountAsync(CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var taskCompletionSource = new TaskCompletionSource(); using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled())) { BridgeConnector.Socket.On("appGetBadgeCountCompleted", (count) => { BridgeConnector.Socket.Off("appGetBadgeCountCompleted"); taskCompletionSource.SetResult((int)count); }); BridgeConnector.Socket.Emit("appGetBadgeCount"); return await taskCompletionSource.Task .ConfigureAwait(false); } } /// /// A object that allows you to read and manipulate the command line arguments that Chromium uses. /// public CommandLine CommandLine { get; internal set; } /// /// Whether the current desktop environment is Unity launcher. /// /// The cancellation token. public async Task IsUnityRunningAsync(CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var taskCompletionSource = new TaskCompletionSource(); using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled())) { BridgeConnector.Socket.On("appIsUnityRunningCompleted", (isUnityRunning) => { BridgeConnector.Socket.Off("appIsUnityRunningCompleted"); taskCompletionSource.SetResult((bool)isUnityRunning); }); BridgeConnector.Socket.Emit("appIsUnityRunning"); return await taskCompletionSource.Task .ConfigureAwait(false); } } /// /// If you provided path and args options to then you need to pass the same /// arguments here for to be set correctly. /// public async Task GetLoginItemSettingsAsync(CancellationToken cancellationToken = default) { return await GetLoginItemSettingsAsync(null, cancellationToken); } /// /// If you provided path and args options to then you need to pass the same /// arguments here for to be set correctly. /// /// /// The cancellation token. public async Task GetLoginItemSettingsAsync(LoginItemSettingsOptions options, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var taskCompletionSource = new TaskCompletionSource(); using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled())) { BridgeConnector.Socket.On("appGetLoginItemSettingsCompleted", (loginItemSettings) => { BridgeConnector.Socket.Off("appGetLoginItemSettingsCompleted"); var result = ((JObject) loginItemSettings).ToObject(); taskCompletionSource.SetResult(result); }); if (options == null) { BridgeConnector.Socket.Emit("appGetLoginItemSettings"); } else { BridgeConnector.Socket.Emit("appGetLoginItemSettings", JObject.FromObject(options, _jsonSerializer)); } return await taskCompletionSource.Task .ConfigureAwait(false); } } /// /// Set the app's login item settings. /// To work with Electron's autoUpdater on Windows, which uses Squirrel, /// you'll want to set the launch path to Update.exe, and pass arguments that specify your application name. /// /// public void SetLoginItemSettings(LoginSettings loginSettings) { BridgeConnector.Socket.Emit("appSetLoginItemSettings", JObject.FromObject(loginSettings, _jsonSerializer)); } /// /// if Chrome's accessibility support is enabled, otherwise. This API will /// return if the use of assistive technologies, such as screen readers, has been detected. /// See Chromium's accessibility docs for more details. /// /// if Chrome’s accessibility support is enabled, otherwise. public async Task IsAccessibilitySupportEnabledAsync(CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var taskCompletionSource = new TaskCompletionSource(); using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled())) { BridgeConnector.Socket.On("appIsAccessibilitySupportEnabledCompleted", (isAccessibilitySupportEnabled) => { BridgeConnector.Socket.Off("appIsAccessibilitySupportEnabledCompleted"); taskCompletionSource.SetResult((bool)isAccessibilitySupportEnabled); }); BridgeConnector.Socket.Emit("appIsAccessibilitySupportEnabled"); return await taskCompletionSource.Task .ConfigureAwait(false); } } /// /// Manually enables Chrome's accessibility support, allowing to expose accessibility switch to users in application settings. /// See Chromium's accessibility docs for more details. /// Disabled () by default. /// /// This API must be called after the event is emitted. /// /// Note: Rendering accessibility tree can significantly affect the performance of your app. It should not be enabled by default. /// /// Enable or disable accessibility tree rendering. public void SetAccessibilitySupportEnabled(bool enabled) { BridgeConnector.Socket.Emit("appSetAboutPanelOptions", enabled); } /// /// Show the app's about panel options. These options can be overridden with /// . /// public void ShowAboutPanel() { BridgeConnector.Socket.Emit("appShowAboutPanel"); } /// /// Set the about panel options. This will override the values defined in the app's .plist file on macOS. See the /// Apple docs /// for more details. On Linux, values must be set in order to be shown; there are no defaults. /// /// If you do not set credits but still wish to surface them in your app, AppKit will look for a file named "Credits.html", /// "Credits.rtf", and "Credits.rtfd", in that order, in the bundle returned by the NSBundle class method main. The first file /// found is used, and if none is found, the info area is left blank. See Apple /// documentation for more information. /// /// About panel options. public void SetAboutPanelOptions(AboutPanelOptions options) { BridgeConnector.Socket.Emit("appSetAboutPanelOptions", JObject.FromObject(options, _jsonSerializer)); } /// /// A which is the user agent string Electron will use as a global fallback. /// /// This is the user agent that will be used when no user agent is set at the webContents or /// session level. It is useful for ensuring that your entire app has the same user agent. Set to a /// custom value as early as possible in your app's initialization to ensure that your overridden value /// is used. /// public string UserAgentFallback { [Obsolete("Use the asynchronous version UserAgentFallbackAsync instead")] get { return UserAgentFallbackAsync.Result; } set { BridgeConnector.Socket.Emit("appSetUserAgentFallback", value); } } /// /// A which is the user agent string Electron will use as a global fallback. /// /// This is the user agent that will be used when no user agent is set at the webContents or /// session level. It is useful for ensuring that your entire app has the same user agent. Set to a /// custom value as early as possible in your app's initialization to ensure that your overridden value /// is used. /// public Task UserAgentFallbackAsync { get { return Task.Run(() => { var taskCompletionSource = new TaskCompletionSource(); BridgeConnector.Socket.On("appGetUserAgentFallbackCompleted", (result) => { BridgeConnector.Socket.Off("appGetUserAgentFallbackCompleted"); taskCompletionSource.SetResult((string)result); }); BridgeConnector.Socket.Emit("appGetUserAgentFallback"); return taskCompletionSource.Task; }); } } internal void PreventQuit() { _preventQuit = true; } private bool _preventQuit = false; private const string ModuleName = "app"; /// /// Subscribe to an unmapped event on the module. /// /// The event name /// The handler public void On(string eventName, Action action) => Events.Instance.On(ModuleName, eventName, action); /// /// Subscribe to an unmapped event on the module. /// /// The event name /// The handler public async Task On(string eventName, Action action) => await Events.Instance.On(ModuleName, eventName, action); /// /// Subscribe to an unmapped event on the module once. /// /// The event name /// The handler public void Once(string eventName, Action action) => Events.Instance.Once(ModuleName, eventName, action); /// /// Subscribe to an unmapped event on the module once. /// /// The event name /// The handler public async Task Once(string eventName, Action action) => await Events.Instance.Once(ModuleName, eventName, action); } }