GPAL - Generally Positive Automation Library v1.0
GPAL The Fluent Automation LIbrary
Loading...
Searching...
No Matches
BrowserSettings.cs
1// =============================================================================
2// GPAL - Generally Positive Automation Library
3// Copyright © 2026 Software Decisions, Inc. All rights reserved.
4//
5// This file is part of GPAL.
6// Licensed under the Business Source License 1.1
7//
8// Primary development, architecture, and vision by Michael B. Vederman,
9// CEO of Software Decisions, Inc., Texas.
10//
11// Internal development maintained privately.
12// Public releases appear on GitHub: https://github.com/SoftwareDecisionsInc/GPAL.
13//
14// See LICENSE for full terms, including Additional Use Grant.
15// =============================================================================
16
17using System;
18using System.Collections.Generic;
19using System.Linq;
20using System.Text;
21using System.Threading.Tasks;
22using System.Windows.Forms;
23using OpenQA.Selenium;
24using static GenerallyPositive.Enums;
25using System.Drawing;
26using System.Xml;
27using System.Diagnostics;
28using System.IO.Pipes;
29using System.IO;
30using Newtonsoft.Json.Linq;
31
33{
34 internal class TabInfo
35 {
36 public string URL { get; set; }
37 public bool ActiveTab { get; set; }
38
39 public TabInfo() { }
40 }
41 internal class URLInfo
42 {
43 public int TabId { get; set; }
44 public bool ActiveTab { get; set; }
45
46 public URLInfo() { }
47 }
48 public class BrowserSettings
49 {
50 internal IBrowser Browser { get; set; }
51 internal bool ObeyRobotsTxt { get; set; } = false;
52 internal bool RespectRobotMetaTags { get; set; } = false;
53 internal List<string> RobotsTxt { get; set; }
54 internal Dictionary<int, TabInfo> TabIdsToURL { get; set; } = new Dictionary<int, TabInfo>(); // tabId > url, activeTab
55 internal Dictionary<string, URLInfo> URLsToTabId { get; set; } = new Dictionary<string, URLInfo>(); // ulr > tabId, activeTab
56 internal Dictionary<string, WindowInfo> WindowIdsToURL { get; set; } = new Dictionary<string, WindowInfo>();
57 // name of this browser instance, set by GPAL factory
58 internal string BrowserName { get; set; } = null;
59 internal string DriverLocation { get; set; } = null;
60 internal int ServiceDriverPid { get; set; } = 0;
61 // load images on a web page?
62 internal bool LoadImages { get; set; } = false;
63 // open pdf documents in external program?
64 internal bool OpenPDFExternally { get; set; } = false;
65 // open file save dialog
66 internal bool PromptForDownload { get; set; } = true;
67
68 // what to answer an alert, confirm or prompt the page raises. null leaves them alone, so they open and
69 // block the way they would for a person. set, and the browser answers them without anyone seeing one
70 internal bool? DialogsAccepted { get; set; } = null;
71
72 // what a prompt hands back when dialogs are accepted. null lets the prompt's own default stand
73 internal string DialogText { get; set; } = null;
74
75 // handlers for failures this browser is asked to decide about. Empty means fall through to the global ones
76 internal List<Browser.CallOnFailDelegate> CallOnFail { get; set; } = new List<Browser.CallOnFailDelegate>();
77
78 // who this browser is, supplied by .WithCredentials, acted on once at the first navigation. Whatever
79 // mechanism carries it, basic and proxy and bearer all persist for the session, so it is presented once
80 internal ICredentials Credentials { get; set; }
81 internal DateTime? CredentialsPresented { get; set; }
82 // filetypes to download, not open
83 internal List<string> DownloadFileTypeList { get; set; } = null;
84 // filetypes to open, not download
85 internal List<string> OpenFileTypeList { get; set; } = null;
86 // when performing an action, do we wait on document.ready?
87 internal bool WaitOnDocumentReady { get; set; } = true;
88 internal bool WaitOnNetworkIdle { get; set; } = false;
89 internal int NetworkIdleTimeoutMs { get; set; } = 30_000;
90 internal int NetworkIdleMaxConnections { get; set; } = 0;
91 internal int NetworkIdlePruneMs { get; set; } = 3_000;
92 internal bool TempProfileCreated { get; set; } = false; // so we know to delete it later
93 internal bool DownloadBehaviorSet { get; set; } = false; // we pointed the browser's downloads at our own directory, so it gets handed back when we close
94 internal Dictionary<string, JToken> PreviousDownloadPreferences { get; set; } = null; // the supplied profile's download settings as we found them, put back once the browser is gone
95
96 internal Dictionary<string, string> PreviousFirefoxPreferences { get; set; } = null; // the same for a firefox profile's user.js, which is where its download and pdf settings live
97 [Obsolete]
98 internal string DefaultProfileDataDirectory { get; set; } // default directory used if chrome is started without a profile (this is a nono)
103 internal string ProfileDataDirectory { get; set; }
104 internal string ProfileName { get; set; }
105 internal string ProfileUserName { get; set; }
106 // current browser type
107 internal BrowserType BrowserType { get; set; } = BrowserType.Chrome;
108 internal string PlatformName { get; set; } = "Windows 10";
109
110 // whether to connect to existing browser to start controlling something on-screen
111 internal bool UseExistingBrowser { get; set; } = false;
112 internal int? ExistingBrowserPort { get; set; } = null;
113
114 // for using/connecting to an already open browser
115 internal int? DebugPort { get; set; }
116 internal bool DebugPipe { get; set; }
117 public PipeStream InboundPipe { get; set; } // Parent writes, Chrome reads
118 public PipeStream OutboundPipe { get; set; } // Parent reads, Chrome writes
119 public PipeStream ErrorPipe { get; set; } // Parent error, Chrome writes
120 internal string PuppeteerUrl { get; set; }
121
122 // asked for by .CaptureCalls, which can be said before there is a browser to say it to. read when the
123 // communicator comes up, so recording starts with the first request the first page makes
124 internal bool CaptureCalls { get; set; } = false;
125
126 // set by .WithCallFilter. when there is one, only calls whose url holds it are recorded, which keeps a
127 // long run from accumulating every image a page asked for
128 internal string CallFilter { get; set; }
129
130 // so an engine that cannot capture says so once rather than on every ask
131 internal bool CaptureUnavailableReported { get; set; } = false;
132
133 PuppeteerCommunicator _puppeteerCommunicator = null;
134
135 // whether there is one already, asked without the getter below building one. reaching for a communicator
136 // before the browser is up starts it connecting to a debug port nothing is listening on yet
137 internal bool HasPuppeteerCommunicator => null != _puppeteerCommunicator;
138
139 internal PuppeteerCommunicator PuppeteerCommunicator
140 {
141 get
142 {
143 if (null == _puppeteerCommunicator && true == UsePuppeteer)
144 {
145 if (null == PuppeteerUrl)
146 {
147 // no port means this browser has not been launched, and the default is not a spare: it is
148 // the port the first browser of the run took. pointed there, this one attaches to that
149 // browser and drives it, and both workflows quietly share a session. a port of our own
150 // that nothing is listening on fails to connect instead, which is the honest answer
151 int ours = BrowserHelper.FindFreePort();
152
153 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $"A Puppeteer connection was asked for before [{BrowserType}] had a debugging port, so it is pointed at [{ours}] where nothing is listening rather than at another browser", this, GPALObjectType.Browser);
154
155 PuppeteerUrl = $"http://localhost:{ours}";
156 }
157
158 // .CaptureCalls can be said before there is a browser, so what was asked for is handed to
159 // the communicator as it is built. it connects and attaches inside its constructor, so
160 // setting this afterwards would be too late for the page it attaches to
161 _puppeteerCommunicator = new PuppeteerCommunicator(PuppeteerUrl, Browser, DebugPipe, CaptureCalls);
162 }
163 else if (null == _puppeteerCommunicator && false == UsePuppeteer)
164 {
165 // for example, if we are in debugger and inspect browser, it will attempt to resolve. built
166 // once like the other one, rather than replaced on every read
167 _puppeteerCommunicator = new PuppeteerCommunicator();
168 }
169
170 return _puppeteerCommunicator;
171 }
172 set
173 {
174 _puppeteerCommunicator = value;
175 }
176 }
177 internal IPuppeteerClient PuppeteerClient
178 {
179 get => PuppeteerCommunicator?.PuppeteerClient;
180 set => PuppeteerCommunicator.PuppeteerClient = value;
181 }
182 internal bool FullScreen { get; set; } = false;
183 internal bool Maximize { get; set; } = false;
184 internal bool Minimize { get; set; } = false;
185 public bool UseHardware { get => AutomationEngine.OttoMagicHW == AutomationEngine || AutomationEngine.PuppeteerPipeHW == AutomationEngine || AutomationEngine.PuppeteerPortHW == AutomationEngine || AutomationEngine.SeleniumHW == AutomationEngine; }
186 public bool UseJavaScript { get => AutomationEngine.OttoMagic == AutomationEngine || AutomationEngine.SeleniumJS == AutomationEngine; }
187 public bool UseOttoMagic { get => AutomationEngine.OttoMagic == AutomationEngine || AutomationEngine.OttoMagicHW == AutomationEngine; }
188 public bool UsePuppeteer { get => AutomationEngine.PuppeteerPipe == AutomationEngine || AutomationEngine.PuppeteerPort == AutomationEngine || AutomationEngine.PuppeteerPortHW == AutomationEngine || AutomationEngine.PuppeteerPipeHW == AutomationEngine; }
189 public bool UseSelenium { get => AutomationEngine.Selenium == AutomationEngine || AutomationEngine.SeleniumJS == AutomationEngine || AutomationEngine.SeleniumHW == AutomationEngine; }
190
191 public AutomationEngine AutomationEngine { get; set; } = AutomationEngine.PuppeteerPort;
192
193 // run on a Win32 desktop object rather than the one the user is looking at
194 internal bool HiddenDesktop { get; set; } = false;
195
196 // which one. browsers naming the same desktop share it, and browsers naming different ones get one each
197 // filled in by WithHiddenDesktop: a name of this browser's own when none was given, or the name it was
198 // given. the value here only stands until one of those is called
199 internal string HiddenDesktopName { get; set; } = GenerallyPositive.Browser.HiddenDesktop.DefaultName;
200
201 // whether what a hidden desktop means for this browser has been worked out and said. WorkflowSetup runs
202 // on every action, and the engine it settles on is worth saying once rather than on all of them
203 internal bool HiddenDesktopReported { get; set; } = false;
204
205 // whether the desktop has been asked what actually arrived on it, which is worth doing once after the
206 // launch rather than on every action afterwards
207 internal bool HiddenDesktopChecked { get; set; } = false;
208 internal int WaitForDocumentReadyTimeoutMs { get; set; } = 30_000;
209
210 // how long a click waits to see whether it started a navigation. Only covers the browser committing the
211 // new url, not loading it - CheckDocumentReady owns the loading half once we know we are going somewhere
212 internal int NavigationGraceMs { get; set; } = 250;
213
214 internal int WithAllThatMatch { get; set; } = int.MaxValue; // all rows, default
215
216 internal string RestApiBaseUrl { get; set; }
217 // true when a workflow handed us the address rather than a browser announcing it, which means the
218 // browser was started by someone else and is not ours to launch, top or kill
219 internal bool AttachedRestApi { get; set; }
220
221 internal IMagicHelper _magicHelper = null;
228 {
229 get => _magicHelper;
230
231 internal set => _magicHelper = value;
232 }
233
241 internal void DetachFromRunningBrowser(Browser browser)
242 {
243 Browser = browser;
244 BrowserDriver = null;
245 Process = null;
246 RestApiBaseUrl = null;
247 _magicHelper = new MagicHelper(browser);
248 _puppeteerCommunicator = null;
249 }
250 internal StealthType StealthType { get; set; } = StealthType.None;
251 internal bool StealthApplied { get; set; } = false;
252 internal string CurrentURL { get; set; }
253 private GPALUrl _currentGPALUrl = null;
254 internal GPALUrl CurrentGPALUrl
255 {
256 get => _currentGPALUrl;
257 set
258 {
259 _currentGPALUrl = value;
260 StorageActionToRun = new StorageAction(); // any time we set this, we reset the action to run, but this doesn't help us clear values from a prior call
261 }
262 }
263 internal List<GPALUrl> GPALUrlList { get; set; }
264 internal string Referrer { get; set; } = null;
265 internal string OverrideUserAgent { get; set; } = null;
266
267 // launch a browser purely to read its real user agent, rather than working one out without one. Off by
268 // default: a headless start should not pay for a browser nobody asked for
269 internal bool UserAgentFromBrowser { get; set; } = false;
270 internal Selector CurrentSelector { get; set; } = null;
271 internal IWebDriver BrowserDriver { get; set; } = null;
272 internal System.Windows.Forms.Form CurrentForm { get; set; } = null;
273 internal int WaitForWindowTimeoutInSeconds { get; set; } = 30; // seconds
274 internal bool BlockPopUps { get; set; } = true;
275 internal int DownloadTimeoutInSec { get; set; } = 10;
276 internal bool UseDirectDownload { get; set; } = false;
277 internal string DownloadLocation { get; set; }
278 internal bool DeleteFileBeforeDownload { get; set; } = false;
279 internal bool OpenInTab { get; set; } = false; // caveat: note: only good for one GoTo
280 internal bool UseHeadless { get; set; } = false;
281 internal bool? FileDownloaded { get; set; }
282 internal bool ScrollIntoView { get; set; } = false;
283 internal Rectangle WindowSize { get; set; }
284 internal Size SavedWindowSize { get; set; }
285 internal PageOrientation PageOrientation { get; set; }
286 internal object JavaScriptResultObj { get; set; }
287 internal string JavaScriptResultStr { get; set; }
288 internal StorageAction StorageActionToRun { get; set; }
289 internal bool StorageActionCalled { get; set; }
290
291 internal string Version = string.Empty;
292 internal int ServerResponseCode { get; set; }
293
294 internal Bitmap ScreenShot;
295 internal Process Process { get; set; } = null; // set when using ottomatic/magic which launches the browser without a driver
296 internal int WhileLoopTimeoutMs { get; set; } = 30000; // Default: 30 seconds
297 internal int WhileLoopMaxIterations { get; set; } = 1000; // Default: 1000 iterations
298 internal List<Action<IBrowser>> Workflows { get; set; }
299 internal BrowserSettings(Browser browser)
300 {
301 DownloadFileTypeList = new List<string>();
302 OpenFileTypeList = new List<string>();
303 WindowSize = new Rectangle();
304 SavedWindowSize = new Size();
305 Workflows = new List<Action<IBrowser>>();
306 Browser = browser;
307 PageOrientation = PageOrientation.Portrait;
308 RobotsTxt = new List<string>();
309 StorageActionToRun = new StorageAction();
310 StorageActionCalled = false;
311 GPALUrlList = new List<GPALUrl>();
312 }
313 }
314}
315
Browser object that contains the fluent methods to create your Browser workflow. Instatiated using G...
Definition Browser.cs:68
IMagicHelper MagicHelper
This browser's own MagicHelper, talking to this browser's OttoMagic port. Assigned when the browser i...
Everything starts here, all the GPAL controls and global settings using fluent syntax are here....
Definition GPAL.cs:49
static void PublishSimpleEvent(GPALEventType gPALEventType, string msg, dynamic gPALObject=null, Enums.GPALObjectType gPALObjectType=GPALObjectType.None, Exception ex=null)
Publish a message to either the information channel or exception channel (if exception passed in) Pub...
Definition GPAL.cs:2406