seb-win-refactoring/SafeExamBrowser.Browser/BrowserWindow.cs

789 lines
26 KiB
C#
Raw Normal View History

/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using CefSharp;
using CefSharp.WinForms.Handler;
using CefSharp.WinForms.Host;
using SafeExamBrowser.Applications.Contracts.Events;
using SafeExamBrowser.Browser.Contracts.Events;
using SafeExamBrowser.Browser.Contracts.Filters;
using SafeExamBrowser.Browser.Events;
using SafeExamBrowser.Browser.Filters;
using SafeExamBrowser.Browser.Handlers;
using SafeExamBrowser.Browser.Wrapper;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Core.Contracts.Resources.Icons;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Settings.Browser;
using SafeExamBrowser.Settings.Browser.Filter;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Browser;
using SafeExamBrowser.UserInterface.Contracts.Browser.Data;
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using Syroot.Windows.IO;
using BrowserSettings = SafeExamBrowser.Settings.Browser.BrowserSettings;
using DisplayHandler = SafeExamBrowser.Browser.Handlers.DisplayHandler;
using Request = SafeExamBrowser.Browser.Contracts.Filters.Request;
using ResourceHandler = SafeExamBrowser.Browser.Handlers.ResourceHandler;
using TitleChangedEventHandler = SafeExamBrowser.Applications.Contracts.Events.TitleChangedEventHandler;
namespace SafeExamBrowser.Browser
{
internal class BrowserWindow : Contracts.IBrowserWindow
{
private const string CLEAR_FIND_TERM = "thisisahacktoclearthesearchresultsasitappearsthatthereisnosuchfunctionalityincef";
private const double ZOOM_FACTOR = 0.2;
private readonly AppConfig appConfig;
private readonly Clipboard clipboard;
private readonly IFileSystemDialog fileSystemDialog;
private readonly IHashAlgorithm hashAlgorithm;
private readonly HttpClient httpClient;
private readonly IKeyGenerator keyGenerator;
private readonly IModuleLogger logger;
private readonly IMessageBox messageBox;
private readonly SessionMode sessionMode;
private readonly Dictionary<int, BrowserWindow> popups;
private readonly BrowserSettings settings;
private readonly string startUrl;
private readonly IText text;
private readonly IUserInterfaceFactory uiFactory;
private (string term, bool isInitial, bool caseSensitive, bool forward) findParameters;
private IBrowserWindow window;
private double zoomLevel;
private WindowSettings WindowSettings
{
get { return IsMainWindow ? settings.MainWindow : settings.AdditionalWindow; }
}
internal IBrowserControl Control { get; private set; }
internal int Id { get; }
public IntPtr Handle { get; private set; }
public IconResource Icon { get; private set; }
public bool IsMainWindow { get; private set; }
public string Title { get; private set; }
public string Url { get; private set; }
internal event WindowClosedEventHandler Closed;
internal event DownloadRequestedEventHandler ConfigurationDownloadRequested;
internal event LoseFocusRequestedEventHandler LoseFocusRequested;
internal event PopupRequestedEventHandler PopupRequested;
internal event ResetRequestedEventHandler ResetRequested;
2019-12-19 15:02:40 +01:00
internal event TerminationRequestedEventHandler TerminationRequested;
internal event UserIdentifierDetectedEventHandler UserIdentifierDetected;
public event IconChangedEventHandler IconChanged;
public event TitleChangedEventHandler TitleChanged;
public BrowserWindow(
AppConfig appConfig,
Clipboard clipboard,
IFileSystemDialog fileSystemDialog,
IHashAlgorithm hashAlgorithm,
int id,
bool isMainWindow,
IKeyGenerator keyGenerator,
IModuleLogger logger,
IMessageBox messageBox,
SessionMode sessionMode,
BrowserSettings settings,
string startUrl,
IText text,
IUserInterfaceFactory uiFactory)
2018-03-14 11:13:30 +01:00
{
this.appConfig = appConfig;
this.clipboard = clipboard;
this.fileSystemDialog = fileSystemDialog;
this.hashAlgorithm = hashAlgorithm;
this.httpClient = new HttpClient();
this.Id = id;
this.IsMainWindow = isMainWindow;
this.keyGenerator = keyGenerator;
this.logger = logger;
this.messageBox = messageBox;
this.popups = new Dictionary<int, BrowserWindow>();
this.sessionMode = sessionMode;
2018-03-14 11:13:30 +01:00
this.settings = settings;
this.startUrl = startUrl;
2018-03-14 11:13:30 +01:00
this.text = text;
this.uiFactory = uiFactory;
}
public void Activate()
{
window.BringToForeground();
}
internal void Close()
{
window.Close();
Control.Destroy();
}
internal void Focus(bool forward)
{
if (forward)
{
window.FocusToolbar(forward);
}
else
{
window.FocusBrowser();
Activate();
}
}
internal void InitializeControl()
{
var cefSharpControl = default(ICefSharpControl);
var controlLogger = logger.CloneFor($"{nameof(BrowserControl)} #{Id}");
var dialogHandler = new DialogHandler();
var displayHandler = new DisplayHandler();
2019-12-19 15:02:40 +01:00
var downloadLogger = logger.CloneFor($"{nameof(DownloadHandler)} #{Id}");
var downloadHandler = new DownloadHandler(appConfig, downloadLogger, settings, WindowSettings);
var keyboardHandler = new KeyboardHandler();
var renderHandler = new RenderProcessMessageHandler(appConfig, clipboard, keyGenerator, settings, text);
var requestFilter = new RequestFilter();
2019-12-19 15:02:40 +01:00
var requestLogger = logger.CloneFor($"{nameof(RequestHandler)} #{Id}");
var resourceHandler = new ResourceHandler(appConfig, requestFilter, keyGenerator, logger, sessionMode, settings, WindowSettings, text);
var requestHandler = new RequestHandler(appConfig, requestFilter, requestLogger, resourceHandler, settings, WindowSettings);
Icon = new BrowserIconResource();
if (IsMainWindow)
{
cefSharpControl = new CefSharpBrowserControl(CreateLifeSpanHandlerForMainWindow(), startUrl);
}
else
{
cefSharpControl = new CefSharpPopupControl();
}
dialogHandler.DialogRequested += DialogHandler_DialogRequested;
displayHandler.FaviconChanged += DisplayHandler_FaviconChanged;
displayHandler.ProgressChanged += DisplayHandler_ProgressChanged;
downloadHandler.ConfigurationDownloadRequested += DownloadHandler_ConfigurationDownloadRequested;
downloadHandler.DownloadAborted += DownloadHandler_DownloadAborted;
downloadHandler.DownloadUpdated += DownloadHandler_DownloadUpdated;
keyboardHandler.FindRequested += KeyboardHandler_FindRequested;
keyboardHandler.FocusAddressBarRequested += KeyboardHandler_FocusAddressBarRequested;
keyboardHandler.HomeNavigationRequested += HomeNavigationRequested;
keyboardHandler.ReloadRequested += ReloadRequested;
keyboardHandler.TabPressed += KeyboardHandler_TabPressed;
keyboardHandler.ZoomInRequested += ZoomInRequested;
keyboardHandler.ZoomOutRequested += ZoomOutRequested;
keyboardHandler.ZoomResetRequested += ZoomResetRequested;
2019-12-19 15:02:40 +01:00
requestHandler.QuitUrlVisited += RequestHandler_QuitUrlVisited;
requestHandler.RequestBlocked += RequestHandler_RequestBlocked;
resourceHandler.UserIdentifierDetected += (id) => UserIdentifierDetected?.Invoke(id);
InitializeRequestFilter(requestFilter);
Control = new BrowserControl(clipboard, cefSharpControl, dialogHandler, displayHandler, downloadHandler, keyboardHandler, controlLogger, renderHandler, requestHandler);
Control.AddressChanged += Control_AddressChanged;
Control.LoadFailed += Control_LoadFailed;
Control.LoadingStateChanged += Control_LoadingStateChanged;
Control.TitleChanged += Control_TitleChanged;
Control.Initialize();
logger.Debug("Initialized browser control.");
}
internal void InitializeWindow()
{
window = uiFactory.CreateBrowserWindow(Control, settings, IsMainWindow, this.logger);
window.AddressChanged += Window_AddressChanged;
window.BackwardNavigationRequested += Window_BackwardNavigationRequested;
window.Closed += Window_Closed;
window.Closing += Window_Closing;
window.DeveloperConsoleRequested += Window_DeveloperConsoleRequested;
window.FindRequested += Window_FindRequested;
window.ForwardNavigationRequested += Window_ForwardNavigationRequested;
window.HomeNavigationRequested += HomeNavigationRequested;
2021-11-24 08:42:07 +01:00
window.LoseFocusRequested += Window_LoseFocusRequested;
window.ReloadRequested += ReloadRequested;
window.ZoomInRequested += ZoomInRequested;
window.ZoomOutRequested += ZoomOutRequested;
window.ZoomResetRequested += ZoomResetRequested;
window.UpdateZoomLevel(CalculateZoomPercentage());
window.Show();
window.BringToForeground();
Handle = window.Handle;
logger.Debug("Initialized browser window.");
}
private ILifeSpanHandler CreateLifeSpanHandlerForMainWindow()
{
return LifeSpanHandler
.Create(() => LifeSpanHandler_CreatePopup())
.OnBeforePopupCreated((wb, b, f, u, t, d, g, s) => LifeSpanHandler_PopupRequested(u))
.OnPopupCreated((c, u) => LifeSpanHandler_PopupCreated(c))
.OnPopupDestroyed((c, b) => LifeSpanHandler_PopupDestroyed(c))
.Build();
}
private void InitializeRequestFilter(IRequestFilter requestFilter)
{
if (settings.Filter.ProcessContentRequests || settings.Filter.ProcessMainRequests)
{
var factory = new RuleFactory();
foreach (var settings in settings.Filter.Rules)
{
var rule = factory.CreateRule(settings.Type);
rule.Initialize(settings);
requestFilter.Load(rule);
}
logger.Debug($"Initialized request filter with {settings.Filter.Rules.Count} rule(s).");
if (requestFilter.Process(new Request { Url = settings.StartUrl }) != FilterResult.Allow)
{
var rule = factory.CreateRule(FilterRuleType.Simplified);
rule.Initialize(new FilterRuleSettings { Expression = settings.StartUrl, Result = FilterResult.Allow });
requestFilter.Load(rule);
logger.Debug($"Automatically created filter rule to allow start URL{(WindowSettings.UrlPolicy.CanLog() ? $" '{settings.StartUrl}'" : "")}.");
}
}
}
private void Control_AddressChanged(string address)
{
logger.Info($"Navigated{(WindowSettings.UrlPolicy.CanLog() ? $" to '{address}'" : "")}.");
Url = address;
window.UpdateAddress(address);
if (WindowSettings.UrlPolicy == UrlPolicy.Always || WindowSettings.UrlPolicy == UrlPolicy.BeforeTitle)
{
Title = address;
window.UpdateTitle(address);
TitleChanged?.Invoke(address);
}
AutoFind();
}
private void Control_LoadFailed(int errorCode, string errorText, bool isMainRequest, string url)
{
switch (errorCode)
{
case (int) CefErrorCode.Aborted:
logger.Info($"Request{(WindowSettings.UrlPolicy.CanLog() ? $" for '{url}'" : "")} was aborted.");
break;
case (int) CefErrorCode.InternetDisconnected:
logger.Info($"Request{(WindowSettings.UrlPolicy.CanLog() ? $" for '{url}'" : "")} has failed due to loss of internet connection.");
break;
case (int) CefErrorCode.None:
logger.Info($"Request{(WindowSettings.UrlPolicy.CanLog() ? $" for '{url}'" : "")} was successful.");
break;
case (int) CefErrorCode.UnknownUrlScheme:
logger.Info($"Request{(WindowSettings.UrlPolicy.CanLog() ? $" for '{url}'" : "")} has an unknown URL scheme and will be handled by the OS.");
break;
default:
HandleUnknownLoadFailure(errorCode, errorText, isMainRequest, url);
break;
}
}
private void HandleUnknownLoadFailure(int errorCode, string errorText, bool isMainRequest, string url)
{
var requestInfo = $"{errorText} ({errorCode}, {(isMainRequest ? "main" : "resource")} request)";
logger.Warn($"Request{(WindowSettings.UrlPolicy.CanLogError() ? $" for '{url}'" : "")} failed: {requestInfo}.");
if (isMainRequest)
{
2020-08-06 14:30:37 +02:00
var title = text.Get(TextKey.Browser_LoadErrorTitle);
var message = text.Get(TextKey.Browser_LoadErrorMessage).Replace("%%URL%%", WindowSettings.UrlPolicy.CanLogError() ? url : "") + $" {requestInfo}";
Task.Run(() => messageBox.Show(message, title, icon: MessageBoxIcon.Error, parent: window)).ContinueWith(_ => Control.NavigateBackwards());
}
}
private void Control_LoadingStateChanged(bool isLoading)
{
window.CanNavigateBackwards = WindowSettings.AllowBackwardNavigation && Control.CanNavigateBackwards;
window.CanNavigateForwards = WindowSettings.AllowForwardNavigation && Control.CanNavigateForwards;
window.UpdateLoadingState(isLoading);
}
private void Control_TitleChanged(string title)
{
if (WindowSettings.UrlPolicy != UrlPolicy.Always)
{
Title = title;
window.UpdateTitle(Title);
TitleChanged?.Invoke(Title);
}
}
private void DialogHandler_DialogRequested(DialogRequestedEventArgs args)
{
var isDownload = args.Operation == FileSystemOperation.Save;
var isUpload = args.Operation == FileSystemOperation.Open;
var isAllowed = (isDownload && settings.AllowDownloads) || (isUpload && settings.AllowUploads);
var initialPath = default(string);
if (isDownload)
{
initialPath = args.InitialPath;
}
else if (string.IsNullOrEmpty(settings.DownAndUploadDirectory))
{
initialPath = KnownFolders.Downloads.ExpandedPath;
}
else
{
initialPath = Environment.ExpandEnvironmentVariables(settings.DownAndUploadDirectory);
}
if (isAllowed)
{
var result = fileSystemDialog.Show(
args.Element,
args.Operation,
initialPath,
title: args.Title,
parent: window,
restrictNavigation: !settings.AllowCustomDownAndUploadLocation,
showElementPath: settings.ShowFileSystemElementPath);
if (result.Success)
{
args.FullPath = result.FullPath;
args.Success = result.Success;
logger.Debug($"User selected path '{result.FullPath}' when asked to {args.Operation}->{args.Element}.");
}
else
{
logger.Debug($"User aborted file system dialog to {args.Operation}->{args.Element}.");
}
}
else
{
logger.Info($"Blocked file system dialog to {args.Operation}->{args.Element}, as {(isDownload ? "downloading" : "uploading")} is not allowed.");
ShowDownUploadNotAllowedMessage(isDownload);
}
}
private void DisplayHandler_FaviconChanged(string uri)
{
Task.Run(() =>
{
var request = new HttpRequestMessage(HttpMethod.Head, uri);
var response = httpClient.SendAsync(request).ContinueWith(task =>
{
if (task.IsCompleted && task.Result.IsSuccessStatusCode)
{
Icon = new BrowserIconResource(uri);
IconChanged?.Invoke(Icon);
window.UpdateIcon(Icon);
}
});
});
}
private void DisplayHandler_ProgressChanged(double value)
{
window.UpdateProgress(value);
}
private void DownloadHandler_ConfigurationDownloadRequested(string fileName, DownloadEventArgs args)
{
if (settings.AllowConfigurationDownloads)
{
logger.Debug($"Forwarding download request for configuration file '{fileName}'.");
ConfigurationDownloadRequested?.Invoke(fileName, args);
if (args.AllowDownload)
{
logger.Debug($"Download request for configuration file '{fileName}' was granted.");
}
else
{
logger.Debug($"Download request for configuration file '{fileName}' was denied.");
messageBox.Show(TextKey.MessageBox_ReconfigurationDenied, TextKey.MessageBox_ReconfigurationDeniedTitle, parent: window);
}
}
else
{
logger.Debug($"Discarded download request for configuration file '{fileName}'.");
}
}
private void DownloadHandler_DownloadAborted()
{
ShowDownUploadNotAllowedMessage();
}
private void DownloadHandler_DownloadUpdated(DownloadItemState state)
{
window.UpdateDownloadState(state);
}
private void HomeNavigationRequested()
{
if (IsMainWindow && (settings.UseStartUrlAsHomeUrl || !string.IsNullOrWhiteSpace(settings.HomeUrl)))
{
var navigate = false;
var url = settings.UseStartUrlAsHomeUrl ? settings.StartUrl : settings.HomeUrl;
if (settings.HomeNavigationRequiresPassword && !string.IsNullOrWhiteSpace(settings.HomePasswordHash))
{
var message = text.Get(TextKey.PasswordDialog_BrowserHomePasswordRequired);
var title = !string.IsNullOrWhiteSpace(settings.HomeNavigationMessage) ? settings.HomeNavigationMessage : text.Get(TextKey.PasswordDialog_BrowserHomePasswordRequiredTitle);
var dialog = uiFactory.CreatePasswordDialog(message, title);
var result = dialog.Show(window);
if (result.Success)
{
var passwordHash = hashAlgorithm.GenerateHashFor(result.Password);
if (settings.HomePasswordHash.Equals(passwordHash, StringComparison.OrdinalIgnoreCase))
{
navigate = true;
}
else
{
messageBox.Show(TextKey.MessageBox_InvalidHomePassword, TextKey.MessageBox_InvalidHomePasswordTitle, icon: MessageBoxIcon.Warning, parent: window);
}
}
}
else
{
var message = text.Get(TextKey.MessageBox_BrowserHomeQuestion);
var title = !string.IsNullOrWhiteSpace(settings.HomeNavigationMessage) ? settings.HomeNavigationMessage : text.Get(TextKey.MessageBox_BrowserHomeQuestionTitle);
var result = messageBox.Show(message, title, MessageBoxAction.YesNo, MessageBoxIcon.Question, window);
navigate = result == MessageBoxResult.Yes;
}
if (navigate)
{
Control.NavigateTo(url);
}
}
}
private void KeyboardHandler_FindRequested()
{
if (settings.AllowFind)
{
window.ShowFindbar();
}
}
private void KeyboardHandler_FocusAddressBarRequested()
2022-05-02 15:18:38 +02:00
{
window.FocusAddressBar();
}
private void KeyboardHandler_TabPressed(bool shiftPressed)
{
Control.ExecuteJavaScript("document.activeElement.tagName", result =>
{
if (result.Result is string tagName && tagName?.ToUpper() == "BODY")
{
// This means the user is now at the start of the focus / tabIndex chain in the website.
if (shiftPressed)
{
window.FocusToolbar(!shiftPressed);
}
else
{
LoseFocusRequested?.Invoke(true);
}
}
});
}
private ChromiumHostControl LifeSpanHandler_CreatePopup()
{
var args = new PopupRequestedEventArgs();
PopupRequested?.Invoke(args);
var control = args.Window.Control.EmbeddableControl as ChromiumHostControl;
var id = control.GetHashCode();
var window = args.Window;
popups[id] = window;
window.Closed += (_) => popups.Remove(id);
return control;
}
private void LifeSpanHandler_PopupCreated(ChromiumHostControl control)
{
var id = control.GetHashCode();
var window = popups[id];
window.InitializeWindow();
}
private void LifeSpanHandler_PopupDestroyed(ChromiumHostControl control)
{
var id = control.GetHashCode();
var window = popups[id];
window.Close();
}
private PopupCreation LifeSpanHandler_PopupRequested(string targetUrl)
{
var creation = PopupCreation.Cancel;
var validCurrentUri = Uri.TryCreate(Control.Address, UriKind.Absolute, out var currentUri);
var validNewUri = Uri.TryCreate(targetUrl, UriKind.Absolute, out var newUri);
var sameHost = validCurrentUri && validNewUri && string.Equals(currentUri.Host, newUri.Host, StringComparison.OrdinalIgnoreCase);
switch (settings.PopupPolicy)
{
case PopupPolicy.Allow:
case PopupPolicy.AllowSameHost when sameHost:
logger.Debug($"Forwarding request to open new window{(WindowSettings.UrlPolicy.CanLog() ? $" for '{targetUrl}'" : "")}...");
creation = PopupCreation.Continue;
break;
case PopupPolicy.AllowSameWindow:
case PopupPolicy.AllowSameHostAndWindow when sameHost:
logger.Info($"Discarding request to open new window and loading{(WindowSettings.UrlPolicy.CanLog() ? $" '{targetUrl}'" : "")} directly...");
Control.NavigateTo(targetUrl);
break;
case PopupPolicy.AllowSameHost when !sameHost:
case PopupPolicy.AllowSameHostAndWindow when !sameHost:
logger.Info($"Blocked request to open new window{(WindowSettings.UrlPolicy.CanLog() ? $" for '{targetUrl}'" : "")} as it targets a different host.");
break;
default:
logger.Info($"Blocked request to open new window{(WindowSettings.UrlPolicy.CanLog() ? $" for '{targetUrl}'" : "")}.");
break;
}
return creation;
}
2019-12-19 15:02:40 +01:00
private void RequestHandler_QuitUrlVisited(string url)
{
Task.Run(() =>
{
if (settings.ResetOnQuitUrl)
2019-12-19 15:02:40 +01:00
{
logger.Info("Forwarding request to reset browser...");
ResetRequested?.Invoke();
}
else
{
if (settings.ConfirmQuitUrl)
2019-12-19 15:02:40 +01:00
{
var message = text.Get(TextKey.MessageBox_BrowserQuitUrlConfirmation);
var title = text.Get(TextKey.MessageBox_BrowserQuitUrlConfirmationTitle);
var result = messageBox.Show(message, title, MessageBoxAction.YesNo, MessageBoxIcon.Question, window);
var terminate = result == MessageBoxResult.Yes;
if (terminate)
{
logger.Info($"User confirmed termination via quit URL{(WindowSettings.UrlPolicy.CanLog() ? $" '{url}'" : "")}, forwarding request...");
TerminationRequested?.Invoke();
}
else
{
logger.Info($"User aborted termination via quit URL{(WindowSettings.UrlPolicy.CanLog() ? $" '{url}'" : "")}.");
}
2019-12-19 15:02:40 +01:00
}
else
{
logger.Info($"Automatically requesting termination due to quit URL{(WindowSettings.UrlPolicy.CanLog() ? $" '{url}'" : "")}...");
TerminationRequested?.Invoke();
2019-12-19 15:02:40 +01:00
}
}
});
}
private void RequestHandler_RequestBlocked(string url)
{
Task.Run(() =>
{
var message = text.Get(TextKey.MessageBox_BrowserNavigationBlocked).Replace("%%URL%%", WindowSettings.UrlPolicy.CanLogError() ? url : "");
var title = text.Get(TextKey.MessageBox_BrowserNavigationBlockedTitle);
Control.TitleChanged -= Control_TitleChanged;
2019-12-19 15:02:40 +01:00
if (url.Equals(startUrl, StringComparison.OrdinalIgnoreCase))
{
window.UpdateTitle($"*** {title} ***");
TitleChanged?.Invoke($"*** {title} ***");
}
messageBox.Show(message, title, parent: window);
Control.TitleChanged += Control_TitleChanged;
});
}
private void ReloadRequested()
{
if (WindowSettings.AllowReloading && WindowSettings.ShowReloadWarning)
{
var result = messageBox.Show(TextKey.MessageBox_ReloadConfirmation, TextKey.MessageBox_ReloadConfirmationTitle, MessageBoxAction.YesNo, MessageBoxIcon.Question, window);
if (result == MessageBoxResult.Yes)
{
logger.Debug("The user confirmed reloading the current page...");
Control.Reload();
}
else
{
logger.Debug("The user aborted reloading the current page.");
}
}
else if (WindowSettings.AllowReloading)
{
logger.Debug("Reloading current page...");
Control.Reload();
}
else
{
logger.Debug("Blocked reload attempt, as the user is not allowed to reload web pages.");
}
}
private void ShowDownUploadNotAllowedMessage(bool isDownload = true)
{
var message = isDownload ? TextKey.MessageBox_DownloadNotAllowed : TextKey.MessageBox_UploadNotAllowed;
var title = isDownload ? TextKey.MessageBox_DownloadNotAllowedTitle : TextKey.MessageBox_UploadNotAllowedTitle;
messageBox.Show(message, title, icon: MessageBoxIcon.Warning, parent: window);
}
private void Window_AddressChanged(string address)
{
var isValid = Uri.TryCreate(address, UriKind.Absolute, out _) || Uri.TryCreate($"https://{address}", UriKind.Absolute, out _);
if (isValid)
{
logger.Debug($"The user requested to navigate to '{address}', the URI is valid.");
Control.NavigateTo(address);
}
else
{
logger.Debug($"The user requested to navigate to '{address}', but the URI is not valid.");
window.UpdateAddress(string.Empty);
}
}
private void Window_BackwardNavigationRequested()
{
logger.Debug("Navigating backwards...");
Control.NavigateBackwards();
}
private void Window_Closing()
{
logger.Debug($"Window is closing...");
}
private void Window_Closed()
{
logger.Debug($"Window has been closed.");
Control.Destroy();
Closed?.Invoke(Id);
}
private void Window_DeveloperConsoleRequested()
{
logger.Debug("Showing developer console...");
Control.ShowDeveloperConsole();
}
private void Window_FindRequested(string term, bool isInitial, bool caseSensitive, bool forward = true)
{
if (settings.AllowFind)
{
findParameters.caseSensitive = caseSensitive;
findParameters.forward = forward;
findParameters.isInitial = isInitial;
findParameters.term = term;
Control.Find(term, isInitial, caseSensitive, forward);
}
}
private void Window_ForwardNavigationRequested()
{
logger.Debug("Navigating forwards...");
Control.NavigateForwards();
}
2021-11-24 08:42:07 +01:00
private void Window_LoseFocusRequested(bool forward)
{
LoseFocusRequested?.Invoke(forward);
}
private void ZoomInRequested()
{
if (settings.AllowPageZoom && CalculateZoomPercentage() < 300)
{
zoomLevel += ZOOM_FACTOR;
Control.Zoom(zoomLevel);
window.UpdateZoomLevel(CalculateZoomPercentage());
logger.Debug($"Increased page zoom to {CalculateZoomPercentage()}%.");
}
}
private void ZoomOutRequested()
{
if (settings.AllowPageZoom && CalculateZoomPercentage() > 25)
{
zoomLevel -= ZOOM_FACTOR;
Control.Zoom(zoomLevel);
window.UpdateZoomLevel(CalculateZoomPercentage());
logger.Debug($"Decreased page zoom to {CalculateZoomPercentage()}%.");
}
}
private void ZoomResetRequested()
{
if (settings.AllowPageZoom)
{
zoomLevel = 0;
Control.Zoom(0);
window.UpdateZoomLevel(CalculateZoomPercentage());
logger.Debug($"Reset page zoom to {CalculateZoomPercentage()}%.");
}
}
private void AutoFind()
{
if (settings.AllowFind && !string.IsNullOrEmpty(findParameters.term) && !CLEAR_FIND_TERM.Equals(findParameters.term, StringComparison.OrdinalIgnoreCase))
{
Control.Find(findParameters.term, findParameters.isInitial, findParameters.caseSensitive, findParameters.forward);
}
}
private double CalculateZoomPercentage()
{
return (zoomLevel * 25.0) + 100.0;
}
}
}