06-17-2023, 06:04 PM
// class "WebBrowserApp.cs"
/// <summary>
/// Opens a web page in current tab in web browser, and waits until loaded.
/// Works with Chrome, Edge, Firefox. Tested 2023-06-17.
/// </summary>
/// <remarks>
/// To open a URL in current tab, uses the address bar's UI element. May need to edit its name in this script or set the <b>AddressBarName</b> property.
/// To detect the loading state, uses the Reload button. May need to edit its name and/or description in this script or set the properties.
///
/// Even when the page is loaded, some UI elements may still be unavailable. Let the script wait for an element, or insert code like `500.ms();`.
/// </remarks>
/// <example>
/// <code><![CDATA[
/// var w = wnd.find(0, "* - Google Chrome", "Chrome_WidgetWin_1");
/// //var w = wnd.find(0, "*- Microsoft Edge", "Chrome_WidgetWin_1");
/// //var w = wnd.find(0, "*Mozilla Firefox", "MozillaWindowClass");
/// var p = new WebBrowserApp(w);
/// p.OpenAndWait("https://www.libreautomate.com/forum");
/// ]]></code>
/// </example>
public class WebBrowserApp {
wnd _w;
elm _eTB, _eAddress, _eReload;
bool _firefox;
//May need to change these strings. They may depend on the computer's language and browser app version.
// Discover them with the "Find UI element" tool.
// Then either change here, or set these properties in scripts that use this class, before the `new WebBrowserApp` line, like `WebBrowserApp.AddressBarName = "name";`.
///
public static string AddressBarName = "**m Address and search bar||Search *or enter address"; //Chrome,Edge||Firefox
///
public static string ReloadButtonName = "**m Reload||Refresh"; //Chrome,Firefox||Edge
///
public static string ReloadButtonDescription = "**m Reload *||Refresh *"; //Chrome||Edge. Not used for Firefox.
//TODO: test with other languages.
//CONSIDER: get desc in ctor.
/// <summary>
/// Finds UI elements for later opening a URL and waiting.
/// </summary>
/// <param name="w">Web browser window.</param>
/// <exception cref="NotFoundException">Web browser UI elements not found. Either <i>w</i> isn't a web browser window, or need to change strings.</exception>
/// <exception cref="AuWndException"><i>w</i> 0 or invalid.</exception>
public WebBrowserApp(wnd w) {
_w = w;
_firefox = _w.ClassNameIs("Mozilla*");
//May need to edit this code. Probably only "Navigation".
_eTB = _w.Elm["TOOLBAR", _firefox ? "Navigation" : null].Find(1);
_eAddress = _eTB.Elm["TEXT", AddressBarName].Find(1);
_eReload = _eTB.Elm["BUTTON", ReloadButtonName].Find(1);
}
/// <summary>
/// Opens a web page in current tab, and waits until loaded or stopped.
/// Also activates the browser window and makes the address bar focused.
/// </summary>
/// <param name="url">Web page URL.</param>
/// <param name="secondsTimeout">How long to wait until loaded. The same as with most other "wait for" functions of the automation library (0 infinite, negative no exception).</param>
/// <returns>false if timeout (when <i>secondsTimeout</i> negative).</returns>
/// <exception cref="TimeoutException"></exception>
/// <exception cref="Exception">Exceptions of used functions.</exception>
public bool OpenAndWait(string url, double secondsTimeout = 60) {
OpenDontWait(url);
return WaitWhileLoading(secondsTimeout);
}
/// <summary>
/// Opens a web page in current tab. Does not wait.
/// Also activates the browser window and makes the address bar focused.
/// </summary>
/// <param name="url">Web page URL.</param>
/// <exception cref="Exception">Exceptions of used functions.</exception>
public void OpenDontWait(string url) {
_w.Activate();
_eAddress.Focus(); //must be before setting Value
_eAddress.Value = url;
_w.Send(0x100, (int)KKey.Enter); //WM_KEYDOWN
_w.Send(0x101, (int)KKey.Enter); //WM_KEYUP
}
/// <summary>
/// Waits while the web browser is loading a web page, until loaded or stopped.
/// </summary>
/// <param name="secondsTimeout">How long to wait until loaded. The same as with most other "wait for" functions of the automation library (0 infinite, negative no exception).</param>
/// <returns>false if timeout (when <i>secondsTimeout</i> negative).</returns>
/// <exception cref="TimeoutException"></exception>
public bool WaitWhileLoading(double secondsTimeout = 60) {
if (_firefox) {
if (!wait.forCondition(-3, () => !_eTB.Elm["BUTTON", ReloadButtonName, "state=!DISABLED"].Exists())) return true; //usually need ~180 ms, but sometimes 500 ms
return null != _eTB.Elm["BUTTON", ReloadButtonName, "state=!DISABLED"].Wait(secondsTimeout);
//Firefox changes and restores the button image and UI element name with a 150-500 ms delay.
// That is why need the first wait.
// And it makes this func ~50% slower than with the fastest Chrome code. But not much slower that with current Chrome code.
// Can't use IA2_DOCUMENT_LOAD_COMPLETE. It is received without a delay, but sometimes in the middle.
// Never mind.
} else {
wildex desc = ReloadButtonDescription;
//return wait.forCondition(secondsTimeout, () => desc.Match(_eReload.Description)); //fastest, but less reliable
int n = 0;
return wait.forCondition(secondsTimeout, () => { if (!desc.Match(_eReload.Description)) n = 0; else if (n++ == 5) return true; return false; });
//The n code adds ~200 ms. Without it may return before all UI elements are available. Eg https://www.libreautomate.com/forum/forumdisplay.php?fid=19.
// To make faster can be used IA2_DOCUMENT_LOAD_COMPLETE, but it is unreliable etc. Worked well when tested with that 1 page, but maybe a coincidence.
}
}
}
Note: may need editing to make it work on your computer. Read comments in the code.