using ElectronNET.API.Entities; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; using System; using System.Threading.Tasks; namespace ElectronNET.API; /// /// Render and control web pages. /// public class WebContents { /// /// Gets the identifier. /// /// /// The identifier. /// public int Id { get; private set; } /// /// Manage browser sessions, cookies, cache, proxy settings, etc. /// public Session Session { get; internal set; } /// /// Emitted when the renderer process crashes or is killed. /// public event Action OnCrashed { add { if (_crashed == null) { BridgeConnector.Socket.On("webContents-crashed" + Id, (killed) => { _crashed((bool)killed); }); BridgeConnector.Socket.Emit("register-webContents-crashed", Id); } _crashed += value; } remove { _crashed -= value; if (_crashed == null) BridgeConnector.Socket.Off("webContents-crashed" + Id); } } private event Action _crashed; /// /// Emitted when the navigation is done, i.e. the spinner of the tab has /// stopped spinning, and the onload event was dispatched. /// public event Action OnDidFinishLoad { add { if (_didFinishLoad == null) { BridgeConnector.Socket.On("webContents-didFinishLoad" + Id, () => { _didFinishLoad(); }); BridgeConnector.Socket.Emit("register-webContents-didFinishLoad", Id); } _didFinishLoad += value; } remove { _didFinishLoad -= value; if (_didFinishLoad == null) BridgeConnector.Socket.Off("webContents-didFinishLoad" + Id); } } private event Action _didFinishLoad; /// /// Emitted when any frame (including main) starts navigating. /// public event Action OnDidStartNavigation { add { if (_didStartNavigation == null) { BridgeConnector.Socket.On("webContents-didStartNavigation" + Id, (url) => { _didStartNavigation(url); }); BridgeConnector.Socket.Emit("register-webContents-didStartNavigation", Id); } _didStartNavigation += value; } remove { _didStartNavigation -= value; if (_didStartNavigation == null) BridgeConnector.Socket.Off("webContents-didStartNavigation" + Id); } } private event Action _didStartNavigation; /// /// Emitted when a main frame navigation is done. /// This event is not emitted for in-page navigations, such as clicking anchor links or updating the window.location.hash. Use did-navigate-in-page event for this purpose. /// public event Action OnDidNavigate { add { if (_didNavigate == null) { BridgeConnector.Socket.On("webContents-didNavigate" + Id, (data) => { _didNavigate(data); }); BridgeConnector.Socket.Emit("register-webContents-didNavigate", Id); } _didNavigate += value; } remove { _didNavigate -= value; if (_didNavigate == null) BridgeConnector.Socket.Off("webContents-didNavigate" + Id); } } private event Action _didNavigate; /// /// Emitted when a server side redirect occurs during navigation. For example a 302 redirect. /// This event will be emitted after OnDidStartNavigation and always before the OnDidRedirectNavigation event for the same navigation. /// public event Action OnWillRedirect { add { if (_willRedirect == null) { BridgeConnector.Socket.On("webContents-willRedirect" + Id, (url) => { _willRedirect(url); }); BridgeConnector.Socket.Emit("register-webContents-willRedirect", Id); } _willRedirect += value; } remove { _willRedirect -= value; if (_willRedirect == null) BridgeConnector.Socket.Off("webContents-willRedirect" + Id); } } private event Action _willRedirect; /// /// Emitted after a server side redirect occurs during navigation. For example a 302 redirect. /// public event Action OnDidRedirectNavigation { add { if (_didRedirectNavigation == null) { BridgeConnector.Socket.On("webContents-didRedirectNavigation" + Id, (url) => { _didRedirectNavigation(url?.ToString()); }); BridgeConnector.Socket.Emit("register-webContents-didRedirectNavigation", Id); } _didRedirectNavigation += value; } remove { _didRedirectNavigation -= value; if (_didRedirectNavigation == null) BridgeConnector.Socket.Off("webContents-didRedirectNavigation" + Id); } } private event Action _didRedirectNavigation; /// /// This event is like OnDidFinishLoad but emitted when the load failed. /// public event Action OnDidFailLoad { add { if (_didFailLoad == null) { BridgeConnector.Socket.On("webContents-willRedirect" + Id, (data) => { _didFailLoad(((JObject) data).ToObject()); }); BridgeConnector.Socket.Emit("register-webContents-willRedirect", Id); } _didFailLoad += value; } remove { _didFailLoad -= value; if (_didFailLoad == null) BridgeConnector.Socket.Off("webContents-willRedirect" + Id); } } private event Action _didFailLoad; /// /// Emitted when an input event is sent to the WebContents. /// public event Action InputEvent { add { if (_inputEvent == null) { BridgeConnector.Socket.On("webContents-input-event" + Id, (eventArgs) => { var inputEvent = ((JObject)eventArgs).ToObject(); _inputEvent(inputEvent); }); BridgeConnector.Socket.Emit("register-webContents-input-event", Id); } _inputEvent += value; } remove { _inputEvent -= value; if (_inputEvent == null) BridgeConnector.Socket.Off("webContents-input-event" + Id); } } private event Action _inputEvent; /// /// Emitted when the document in the top-level frame is loaded. /// public event Action OnDomReady { add { if (_domReady == null) { BridgeConnector.Socket.On("webContents-domReady" + Id, () => { _domReady(); }); BridgeConnector.Socket.Emit("register-webContents-domReady", Id); } _domReady += value; } remove { _domReady -= value; if (_domReady == null) BridgeConnector.Socket.Off("webContents-domReady" + Id); } } private event Action _domReady; internal WebContents(int id) { Id = id; Session = new Session(id); } /// /// Opens the devtools. /// public void OpenDevTools() { BridgeConnector.Socket.Emit("webContentsOpenDevTools", Id); } /// /// Opens the devtools. /// /// public void OpenDevTools(OpenDevToolsOptions openDevToolsOptions) { BridgeConnector.Socket.Emit("webContentsOpenDevTools", Id, JObject.FromObject(openDevToolsOptions, _jsonSerializer)); } /// /// Get system printers. /// /// printers public Task GetPrintersAsync() { var taskCompletionSource = new TaskCompletionSource(); BridgeConnector.Socket.On("webContents-getPrinters-completed", (printers) => { BridgeConnector.Socket.Off("webContents-getPrinters-completed"); taskCompletionSource.SetResult(((Newtonsoft.Json.Linq.JArray)printers).ToObject()); }); BridgeConnector.Socket.Emit("webContents-getPrinters", Id); return taskCompletionSource.Task; } /// /// Prints window's web page. /// /// /// success public Task PrintAsync(PrintOptions options = null) { var taskCompletionSource = new TaskCompletionSource(); BridgeConnector.Socket.On("webContents-print-completed", (success) => { BridgeConnector.Socket.Off("webContents-print-completed"); taskCompletionSource.SetResult((bool)success); }); if(options == null) { BridgeConnector.Socket.Emit("webContents-print", Id, ""); } else { BridgeConnector.Socket.Emit("webContents-print", Id, JObject.FromObject(options, _jsonSerializer)); } return taskCompletionSource.Task; } /// /// Prints window's web page as PDF with Chromium's preview printing custom /// settings.The landscape will be ignored if @page CSS at-rule is used in the web page. /// By default, an empty options will be regarded as: Use page-break-before: always; /// CSS style to force to print to a new page. /// /// /// /// success public Task PrintToPDFAsync(string path, PrintToPDFOptions options = null) { var taskCompletionSource = new TaskCompletionSource(); BridgeConnector.Socket.On("webContents-printToPDF-completed", (success) => { BridgeConnector.Socket.Off("webContents-printToPDF-completed"); taskCompletionSource.SetResult((bool)success); }); if(options == null) { BridgeConnector.Socket.Emit("webContents-printToPDF", Id, "", path); } else { BridgeConnector.Socket.Emit("webContents-printToPDF", Id, JObject.FromObject(options, _jsonSerializer), path); } return taskCompletionSource.Task; } /// /// Evaluates script code in page. /// /// The code to execute. /// if set to true simulate a user gesture. /// The result of the executed code. /// /// /// In the browser window some HTML APIs like `requestFullScreen` can only be /// invoked by a gesture from the user. Setting `userGesture` to `true` will remove /// this limitation. /// /// /// Code execution will be suspended until web page stop loading. /// /// public Task ExecuteJavaScriptAsync(string code, bool userGesture = false) { var taskCompletionSource = new TaskCompletionSource(); BridgeConnector.Socket.On("webContents-executeJavaScript-completed", (result) => { BridgeConnector.Socket.Off("webContents-executeJavaScript-completed"); taskCompletionSource.SetResult(result); }); BridgeConnector.Socket.Emit("webContents-executeJavaScript", Id, code, userGesture); return taskCompletionSource.Task; } /// /// Is used to get the Url of the loaded page. /// It's usefull if a web-server redirects you and you need to know where it redirects. For instance, It's useful in case of Implicit Authorization. /// /// URL of the loaded page public Task GetUrl() { var taskCompletionSource = new TaskCompletionSource(); var eventString = "webContents-getUrl" + Id; BridgeConnector.Socket.On(eventString, (url) => { BridgeConnector.Socket.Off(eventString); taskCompletionSource.SetResult((string)url); }); BridgeConnector.Socket.Emit("webContents-getUrl", Id); return taskCompletionSource.Task; } /// /// The async method will resolve when the page has finished loading, /// and rejects if the page fails to load. /// /// A noop rejection handler is already attached, which avoids unhandled rejection /// errors. /// /// Loads the `url` in the window. The `url` must contain the protocol prefix, e.g. /// the `http://` or `file://`. If the load should bypass http cache then use the /// `pragma` header to achieve it. /// /// public Task LoadURLAsync(string url) { return LoadURLAsync(url, new LoadURLOptions()); } /// /// The async method will resolve when the page has finished loading, /// and rejects if the page fails to load. /// /// A noop rejection handler is already attached, which avoids unhandled rejection /// errors. /// /// Loads the `url` in the window. The `url` must contain the protocol prefix, e.g. /// the `http://` or `file://`. If the load should bypass http cache then use the /// `pragma` header to achieve it. /// /// /// public Task LoadURLAsync(string url, LoadURLOptions options) { var taskCompletionSource = new TaskCompletionSource(); BridgeConnector.Socket.On("webContents-loadURL-complete" + Id, () => { BridgeConnector.Socket.Off("webContents-loadURL-complete" + Id); BridgeConnector.Socket.Off("webContents-loadURL-error" + Id); taskCompletionSource.SetResult(null); }); BridgeConnector.Socket.On("webContents-loadURL-error" + Id, (error) => { BridgeConnector.Socket.Off("webContents-loadURL-error" + Id); taskCompletionSource.SetException(new InvalidOperationException(error.ToString())); }); BridgeConnector.Socket.Emit("webContents-loadURL", Id, url, JObject.FromObject(options, _jsonSerializer)); return taskCompletionSource.Task; } /// /// Inserts CSS into the web page. /// See: https://www.electronjs.org/docs/api/web-contents#contentsinsertcsscss-options /// Works for both BrowserWindows and BrowserViews. /// /// Whether the webContents belong to a BrowserWindow or not (the other option is a BrowserView) /// Absolute path to the CSS file location public void InsertCSS(bool isBrowserWindow, string path) { BridgeConnector.Socket.Emit("webContents-insertCSS", Id, isBrowserWindow, path); } private readonly JsonSerializer _jsonSerializer = new() { ContractResolver = new CamelCasePropertyNamesContractResolver(), NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore }; }