diff --git a/.editorconfig b/.editorconfig index 9a0b2971..0f2ef727 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,10 +5,130 @@ root = true [*] end_of_line = crlf +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = false:none +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion [*.cs] dotnet_style_object_initializer = false:none indent_style = tab +csharp_indent_labels = one_less_than_current +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = true:silent +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_throw_expression = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent +csharp_style_unused_value_assignment_preference = discard_variable:silent +csharp_prefer_static_local_function = true:suggestion +csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent +csharp_style_conditional_delegate_call = true:suggestion +csharp_style_prefer_switch_expression = true:suggestion +csharp_style_prefer_pattern_matching = true:silent +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_extended_property_pattern = true:suggestion +csharp_style_var_for_built_in_types = false:silent +csharp_style_var_when_type_is_apparent = false:silent +csharp_style_var_elsewhere = false:silent +csharp_space_after_cast = true [*.xml] -indent_style = space \ No newline at end of file +indent_style = space +[*.{cs,vb}] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_style_readonly_field = true:suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_allow_multiple_blank_lines_experimental = true:silent +dotnet_style_allow_statement_immediately_after_block_experimental = true:silent +dotnet_code_quality_unused_parameters = all:suggestion +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent diff --git a/SafeExamBrowser.Browser.UnitTests/Handlers/DialogHandlerTests.cs b/SafeExamBrowser.Browser.UnitTests/Handlers/DialogHandlerTests.cs index 0e75f884..08cd9248 100644 --- a/SafeExamBrowser.Browser.UnitTests/Handlers/DialogHandlerTests.cs +++ b/SafeExamBrowser.Browser.UnitTests/Handlers/DialogHandlerTests.cs @@ -31,7 +31,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers [TestMethod] public void MustCorrectlyCancelDialog() { - RequestDialog(default(CefFileDialogMode), false); + RequestDialog(default, false); } [TestMethod] @@ -80,7 +80,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers threadId = Thread.CurrentThread.ManagedThreadId; }; - var status = sut.OnFileDialog(default(IWebBrowser), default(IBrowser), mode, default(CefFileDialogFlags), title, initialPath, default(List), default(int), callback.Object); + var status = sut.OnFileDialog(default, default, mode, default, title, initialPath, default, default, callback.Object); sync.WaitOne(); diff --git a/SafeExamBrowser.Browser.UnitTests/Handlers/DisplayHandlerTests.cs b/SafeExamBrowser.Browser.UnitTests/Handlers/DisplayHandlerTests.cs index 8e4eb994..f0a6b9b7 100644 --- a/SafeExamBrowser.Browser.UnitTests/Handlers/DisplayHandlerTests.cs +++ b/SafeExamBrowser.Browser.UnitTests/Handlers/DisplayHandlerTests.cs @@ -6,11 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -using System; using System.Collections.Generic; -using CefSharp; -using CefSharp.Enums; -using CefSharp.Structs; using Microsoft.VisualStudio.TestTools.UnitTesting; using SafeExamBrowser.Browser.Handlers; @@ -32,10 +28,10 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers { var text = default(string); - Assert.IsFalse(sut.OnAutoResize(default(IWebBrowser), default(IBrowser), default(Size))); - Assert.IsFalse(sut.OnConsoleMessage(default(IWebBrowser), default(ConsoleMessageEventArgs))); - Assert.IsFalse(sut.OnCursorChange(default(IWebBrowser), default(IBrowser), default(IntPtr), default(CursorType), default(CursorInfo))); - Assert.IsFalse(sut.OnTooltipChanged(default(IWebBrowser), ref text)); + Assert.IsFalse(sut.OnAutoResize(default, default, default)); + Assert.IsFalse(sut.OnConsoleMessage(default, default)); + Assert.IsFalse(sut.OnCursorChange(default, default, default, default, default)); + Assert.IsFalse(sut.OnTooltipChanged(default, ref text)); } [TestMethod] @@ -50,12 +46,12 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers called = true; url = u; }; - sut.OnFaviconUrlChange(default(IWebBrowser), default(IBrowser), new List()); + sut.OnFaviconUrlChange(default, default, new List()); - Assert.AreEqual(default(string), url); + Assert.AreEqual(default, url); Assert.IsFalse(called); - sut.OnFaviconUrlChange(default(IWebBrowser), default(IBrowser), new List { newUrl }); + sut.OnFaviconUrlChange(default, default, new List { newUrl }); Assert.AreEqual(newUrl, url); Assert.IsTrue(called); @@ -68,7 +64,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers var actual = default(double); sut.ProgressChanged += (p) => actual = p; - sut.OnLoadingProgressChange(default(IWebBrowser), default(IBrowser), expected); + sut.OnLoadingProgressChange(default, default, expected); Assert.AreEqual(expected, actual); } diff --git a/SafeExamBrowser.Browser.UnitTests/Handlers/LifeSpanHandlerTests.cs b/SafeExamBrowser.Browser.UnitTests/Handlers/LifeSpanHandlerTests.cs deleted file mode 100644 index fdfdb44a..00000000 --- a/SafeExamBrowser.Browser.UnitTests/Handlers/LifeSpanHandlerTests.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) - * - * 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 CefSharp; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SafeExamBrowser.Browser.Events; -using SafeExamBrowser.Browser.Handlers; - -namespace SafeExamBrowser.Browser.UnitTests.Handlers -{ - [TestClass] - public class LifeSpanHandlerTests - { - private LifeSpanHandler sut; - - [TestInitialize] - public void Initialize() - { - sut = new LifeSpanHandler(); - } - - [TestMethod] - public void MustUseDefaultBehavior() - { - Assert.IsFalse(sut.DoClose(default(IWebBrowser), default(IBrowser))); - } - - [TestMethod] - public void MustHandlePopup() - { - var args = default(PopupRequestedEventArgs); - var jsAccess = false; - var url = "https://www.host.org/some-url"; - - sut.PopupRequested += (a) => args = a; - - var result = sut.OnBeforePopup(default(IWebBrowser), default(IBrowser), default(IFrame), url, default(string), default(WindowOpenDisposition), default(bool), default(IPopupFeatures), default(IWindowInfo), default(IBrowserSettings), ref jsAccess, out var newBrowser); - - Assert.IsTrue(result); - Assert.AreEqual(default(IWebBrowser), newBrowser); - Assert.AreEqual(url, args.Url); - } - } -} diff --git a/SafeExamBrowser.Browser.UnitTests/Handlers/RequestHandlerTests.cs b/SafeExamBrowser.Browser.UnitTests/Handlers/RequestHandlerTests.cs index 12518b16..ad0cf5e0 100644 --- a/SafeExamBrowser.Browser.UnitTests/Handlers/RequestHandlerTests.cs +++ b/SafeExamBrowser.Browser.UnitTests/Handlers/RequestHandlerTests.cs @@ -50,16 +50,16 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers text = new Mock(); resourceHandler = new ResourceHandler(appConfig, filter.Object, keyGenerator.Object, logger.Object, settings, windowSettings, text.Object); - sut = new TestableRequestHandler(appConfig, filter.Object, logger.Object, resourceHandler, settings, windowSettings, text.Object); + sut = new TestableRequestHandler(appConfig, filter.Object, logger.Object, resourceHandler, settings, windowSettings); } [TestMethod] public void MustBlockSpecialWindowDispositions() { - Assert.IsTrue(sut.OnOpenUrlFromTab(default(IWebBrowser), default(IBrowser), default(IFrame), default(string), WindowOpenDisposition.NewBackgroundTab, default(bool))); - Assert.IsTrue(sut.OnOpenUrlFromTab(default(IWebBrowser), default(IBrowser), default(IFrame), default(string), WindowOpenDisposition.NewPopup, default(bool))); - Assert.IsTrue(sut.OnOpenUrlFromTab(default(IWebBrowser), default(IBrowser), default(IFrame), default(string), WindowOpenDisposition.NewWindow, default(bool))); - Assert.IsTrue(sut.OnOpenUrlFromTab(default(IWebBrowser), default(IBrowser), default(IFrame), default(string), WindowOpenDisposition.SaveToDisk, default(bool))); + Assert.IsTrue(sut.OnOpenUrlFromTab(default, default, default, default, WindowOpenDisposition.NewBackgroundTab, default)); + Assert.IsTrue(sut.OnOpenUrlFromTab(default, default, default, default, WindowOpenDisposition.NewPopup, default)); + Assert.IsTrue(sut.OnOpenUrlFromTab(default, default, default, default, WindowOpenDisposition.NewWindow, default)); + Assert.IsTrue(sut.OnOpenUrlFromTab(default, default, default, default, WindowOpenDisposition.SaveToDisk, default)); } [TestMethod] @@ -214,7 +214,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers public void MustReturnResourceHandler() { var disableDefaultHandling = default(bool); - var handler = sut.GetResourceRequestHandler(default(IWebBrowser), default(IBrowser), default(IFrame), default(IRequest), default(bool), default(bool), default(string), ref disableDefaultHandling); + var handler = sut.GetResourceRequestHandler(default, default, default, default, default, default, default, ref disableDefaultHandling); Assert.AreSame(resourceHandler, handler); } @@ -229,7 +229,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers settings.Proxy.Proxies.Add(proxy1); settings.Proxy.Proxies.Add(proxy2); - var result = sut.GetAuthCredentials(Mock.Of(), Mock.Of(), default(string), true, "WWW.tEst.Com", 10, default(string), default(string), callback.Object); + var result = sut.GetAuthCredentials(Mock.Of(), Mock.Of(), default, true, "WWW.tEst.Com", 10, default, default, callback.Object); callback.Verify(c => c.Cancel(), Times.Never); callback.Verify(c => c.Continue(It.Is(u => u.Equals(proxy1.Username)), It.Is(p => p.Equals(proxy1.Password))), Times.Once); @@ -243,7 +243,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers { var callback = new Mock(); - sut.GetAuthCredentials(Mock.Of(), Mock.Of(), default(string), false, default(string), default(int), default(string), default(string), callback.Object); + sut.GetAuthCredentials(Mock.Of(), Mock.Of(), default, false, default, default, default, default, callback.Object); callback.Verify(c => c.Cancel(), Times.Never); callback.Verify(c => c.Continue(It.IsAny(), It.IsAny()), Times.Never); @@ -251,14 +251,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers private class TestableRequestHandler : RequestHandler { - internal TestableRequestHandler( - AppConfig appConfig, - IRequestFilter filter, - ILogger logger, - ResourceHandler resourceHandler, - BrowserSettings settings, - WindowSettings windowSettings, - IText text) : base(appConfig, filter, logger, resourceHandler, settings, windowSettings, text) + internal TestableRequestHandler(AppConfig appConfig, IRequestFilter filter, ILogger logger, ResourceHandler resourceHandler, BrowserSettings settings, WindowSettings windowSettings) : base(appConfig, filter, logger, resourceHandler, settings, windowSettings) { } diff --git a/SafeExamBrowser.Browser.UnitTests/SafeExamBrowser.Browser.UnitTests.csproj b/SafeExamBrowser.Browser.UnitTests/SafeExamBrowser.Browser.UnitTests.csproj index ae063877..2c4a4d77 100644 --- a/SafeExamBrowser.Browser.UnitTests/SafeExamBrowser.Browser.UnitTests.csproj +++ b/SafeExamBrowser.Browser.UnitTests/SafeExamBrowser.Browser.UnitTests.csproj @@ -1,8 +1,8 @@  - - - + + + @@ -64,11 +64,11 @@ ..\packages\Castle.Core.4.4.1\lib\net45\Castle.Core.dll - - ..\packages\CefSharp.Common.97.1.11\lib\net452\CefSharp.dll + + ..\packages\CefSharp.Common.98.1.210\lib\net452\CefSharp.dll - - ..\packages\CefSharp.Common.97.1.11\lib\net452\CefSharp.Core.dll + + ..\packages\CefSharp.Common.98.1.210\lib\net452\CefSharp.Core.dll ..\packages\MSTest.TestFramework.2.2.8\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll @@ -101,7 +101,6 @@ - @@ -152,11 +151,11 @@ - - - - + + + + - + \ No newline at end of file diff --git a/SafeExamBrowser.Browser.UnitTests/app.config b/SafeExamBrowser.Browser.UnitTests/app.config index 5c15ee09..423b5a71 100644 --- a/SafeExamBrowser.Browser.UnitTests/app.config +++ b/SafeExamBrowser.Browser.UnitTests/app.config @@ -16,11 +16,11 @@ - + - + diff --git a/SafeExamBrowser.Browser.UnitTests/packages.config b/SafeExamBrowser.Browser.UnitTests/packages.config index 87b76686..ecfc4e86 100644 --- a/SafeExamBrowser.Browser.UnitTests/packages.config +++ b/SafeExamBrowser.Browser.UnitTests/packages.config @@ -1,9 +1,9 @@  - - - + + + diff --git a/SafeExamBrowser.Browser/BrowserApplication.cs b/SafeExamBrowser.Browser/BrowserApplication.cs index 25a135bf..c98f4924 100644 --- a/SafeExamBrowser.Browser/BrowserApplication.cs +++ b/SafeExamBrowser.Browser/BrowserApplication.cs @@ -24,7 +24,6 @@ using SafeExamBrowser.Configuration.Contracts.Cryptography; using SafeExamBrowser.Core.Contracts.Resources.Icons; using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; -using SafeExamBrowser.Settings.Browser; using SafeExamBrowser.Settings.Browser.Proxy; using SafeExamBrowser.Settings.Logging; using SafeExamBrowser.UserInterface.Contracts; @@ -170,36 +169,45 @@ namespace SafeExamBrowser.Browser Thread.Sleep(500); } - private void CreateNewWindow(string url = null) + private void CreateNewWindow(PopupRequestedEventArgs args = default) { var id = ++windowIdCounter; var isMainWindow = windows.Count == 0; - var startUrl = url ?? GenerateStartUrl(); + var startUrl = GenerateStartUrl(); var windowLogger = logger.CloneFor($"Browser Window #{id}"); var window = new BrowserWindow( appConfig, - settings, - id, - isMainWindow, fileSystemDialog, hashAlgorithm, + id, + isMainWindow, keyGenerator, - messageBox, windowLogger, + messageBox, + settings, + startUrl, text, - uiFactory, - startUrl); + uiFactory); window.Closed += Window_Closed; - window.ConfigurationDownloadRequested += (fileName, args) => ConfigurationDownloadRequested?.Invoke(fileName, args); + window.ConfigurationDownloadRequested += (f, a) => ConfigurationDownloadRequested?.Invoke(f, a); window.PopupRequested += Window_PopupRequested; window.ResetRequested += Window_ResetRequested; window.SessionIdentifierDetected += (i) => SessionIdentifierDetected?.Invoke(i); window.TerminationRequested += () => TerminationRequested?.Invoke(); - window.Initialize(); + window.InitializeControl(); windows.Add(window); + if (args != default(PopupRequestedEventArgs)) + { + args.Window = window; + } + else + { + window.InitializeWindow(); + } + logger.Info($"Created browser window #{window.Id}."); WindowsChanged?.Invoke(); } @@ -421,8 +429,8 @@ namespace SafeExamBrowser.Browser private void Window_PopupRequested(PopupRequestedEventArgs args) { - logger.Info($"Received request to create new window{(settings.AdditionalWindow.UrlPolicy.CanLog() ? $" for '{args.Url}'" : "")}..."); - CreateNewWindow(args.Url); + logger.Info($"Received request to create new window..."); + CreateNewWindow(args); } private void Window_ResetRequested() diff --git a/SafeExamBrowser.Browser/BrowserControl.cs b/SafeExamBrowser.Browser/BrowserControl.cs index 5063c7da..56104313 100644 --- a/SafeExamBrowser.Browser/BrowserControl.cs +++ b/SafeExamBrowser.Browser/BrowserControl.cs @@ -8,8 +8,9 @@ using System; using CefSharp; -using CefSharp.WinForms; using SafeExamBrowser.Browser.Content; +using SafeExamBrowser.Browser.Wrapper; +using SafeExamBrowser.Browser.Wrapper.Events; using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Configuration.Contracts.Cryptography; using SafeExamBrowser.I18n.Contracts; @@ -18,105 +19,80 @@ using SafeExamBrowser.UserInterface.Contracts.Browser.Events; namespace SafeExamBrowser.Browser { - internal class BrowserControl : ChromiumWebBrowser, IBrowserControl + internal class BrowserControl : IBrowserControl { private readonly AppConfig appConfig; private readonly ContentLoader contentLoader; - private readonly IContextMenuHandler contextMenuHandler; + private readonly ICefSharpControl control; private readonly IDialogHandler dialogHandler; private readonly IDisplayHandler displayHandler; private readonly IDownloadHandler downloadHandler; - private readonly IKeyGenerator generator; private readonly IKeyboardHandler keyboardHandler; - private readonly ILifeSpanHandler lifeSpanHandler; + private readonly IKeyGenerator generator; private readonly IRequestHandler requestHandler; - private readonly IText text; - private AddressChangedEventHandler addressChanged; - private LoadFailedEventHandler loadFailed; - private LoadingStateChangedEventHandler loadingStateChanged; - private TitleChangedEventHandler titleChanged; + public string Address => control.Address; + public bool CanNavigateBackwards => control.BrowserCore.CanGoBack; + public bool CanNavigateForwards => control.BrowserCore.CanGoForward; + public object EmbeddableControl => control; - public bool CanNavigateBackwards => GetBrowser().CanGoBack; - public bool CanNavigateForwards => GetBrowser().CanGoForward; - - event AddressChangedEventHandler IBrowserControl.AddressChanged - { - add { addressChanged += value; } - remove { addressChanged -= value; } - } - - event LoadFailedEventHandler IBrowserControl.LoadFailed - { - add { loadFailed += value; } - remove { loadFailed -= value; } - } - - event LoadingStateChangedEventHandler IBrowserControl.LoadingStateChanged - { - add { loadingStateChanged += value; } - remove { loadingStateChanged -= value; } - } - - event TitleChangedEventHandler IBrowserControl.TitleChanged - { - add { titleChanged += value; } - remove { titleChanged -= value; } - } + public event AddressChangedEventHandler AddressChanged; + public event LoadFailedEventHandler LoadFailed; + public event LoadingStateChangedEventHandler LoadingStateChanged; + public event TitleChangedEventHandler TitleChanged; public BrowserControl( AppConfig appConfig, - IContextMenuHandler contextMenuHandler, + ICefSharpControl control, IDialogHandler dialogHandler, IDisplayHandler displayHandler, IDownloadHandler downloadHandler, - IKeyGenerator generator, IKeyboardHandler keyboardHandler, - ILifeSpanHandler lifeSpanHandler, + IKeyGenerator generator, IRequestHandler requestHandler, - IText text, - string url) : base(url) + IText text) { this.appConfig = appConfig; this.contentLoader = new ContentLoader(text); - this.contextMenuHandler = contextMenuHandler; + this.control = control; this.dialogHandler = dialogHandler; this.displayHandler = displayHandler; this.downloadHandler = downloadHandler; - this.generator = generator; this.keyboardHandler = keyboardHandler; - this.lifeSpanHandler = lifeSpanHandler; + this.generator = generator; this.requestHandler = requestHandler; - this.text = text; } public void Destroy() { - if (!IsDisposed) + if (!control.IsDisposed) { - Dispose(true); + control.Dispose(true); } } public void Initialize() { - AddressChanged += (o, args) => addressChanged?.Invoke(args.Address); - FrameLoadStart += BrowserControl_FrameLoadStart; - IsBrowserInitializedChanged += BrowserControl_IsBrowserInitializedChanged; - LoadError += BrowserControl_LoadError; - LoadingStateChanged += (o, args) => loadingStateChanged?.Invoke(args.IsLoading); - TitleChanged += (o, args) => titleChanged?.Invoke(args.Title); - - DialogHandler = dialogHandler; - DisplayHandler = displayHandler; - DownloadHandler = downloadHandler; - KeyboardHandler = keyboardHandler; - LifeSpanHandler = lifeSpanHandler; - MenuHandler = contextMenuHandler; - RequestHandler = requestHandler; + control.AddressChanged += (o, e) => AddressChanged?.Invoke(e.Address); + control.AuthCredentialsRequired += (w, b, o, i, h, p, r, s, c, a) => a.Value = requestHandler.GetAuthCredentials(w, b, o, i, h, p, r, s, c); + control.BeforeBrowse += (w, b, f, r, u, i, a) => a.Value = requestHandler.OnBeforeBrowse(w, b, f, r, u, i); + control.BeforeDownload += (w, b, d, c) => downloadHandler.OnBeforeDownload(w, b, d, c); + control.DownloadUpdated += (w, b, d, c) => downloadHandler.OnDownloadUpdated(w, b, d, c); + control.FaviconUrlChanged += (w, b, u) => displayHandler.OnFaviconUrlChange(w, b, u); + control.FileDialogRequested += (w, b, m, f, t, d, a, s, c) => dialogHandler.OnFileDialog(w, b, m, f, t, d, a, s, c); + control.FrameLoadStart += Control_FrameLoadStart; + control.IsBrowserInitializedChanged += Control_IsBrowserInitializedChanged; + control.KeyEvent += (w, b, t, k, n, m, s) => keyboardHandler.OnKeyEvent(w, b, t, k, n, m, s); + control.LoadError += (o, e) => LoadFailed?.Invoke((int) e.ErrorCode, e.ErrorText, e.FailedUrl); + control.LoadingProgressChanged += (w, b, p) => displayHandler.OnLoadingProgressChange(w, b, p); + control.LoadingStateChanged += (o, e) => LoadingStateChanged?.Invoke(e.IsLoading); + control.OpenUrlFromTab += (w, b, f, u, t, g, a) => a.Value = requestHandler.OnOpenUrlFromTab(w, b, f, u, t, g); + control.PreKeyEvent += (IWebBrowser w, IBrowser b, KeyType t, int k, int n, CefEventFlags m, bool i, ref bool s, GenericEventArgs a) => a.Value = keyboardHandler.OnPreKeyEvent(w, b, t, k, n, m, i, ref s); + control.ResourceRequestHandlerRequired += (IWebBrowser w, IBrowser b, IFrame f, IRequest r, bool n, bool d, string i, ref bool h, ResourceRequestEventArgs a) => a.Handler = requestHandler.GetResourceRequestHandler(w, b, f, r, n, d, i, ref h); + control.TitleChanged += (o, e) => TitleChanged?.Invoke(e.Title); } - private void BrowserControl_FrameLoadStart(object sender, FrameLoadStartEventArgs e) + private void Control_FrameLoadStart(object sender, FrameLoadStartEventArgs e) { var browserExamKey = generator.CalculateBrowserExamKeyHash(e.Url); var configurationKey = generator.CalculateConfigurationKeyHash(e.Url); @@ -125,52 +101,47 @@ namespace SafeExamBrowser.Browser e.Frame.ExecuteJavaScriptAsync(api); } + private void Control_IsBrowserInitializedChanged(object sender, EventArgs e) + { + if (control.IsBrowserInitialized) + { + control.BrowserCore.GetHost().SetFocus(true); + } + } + public void Find(string term, bool isInitial, bool caseSensitive, bool forward = true) { - this.Find(0, term, forward, caseSensitive, !isInitial); + control.Find(0, term, forward, caseSensitive, !isInitial); } public void NavigateBackwards() { - GetBrowser().GoBack(); + control.BrowserCore.GoBack(); } public void NavigateForwards() { - GetBrowser().GoForward(); + control.BrowserCore.GoForward(); } public void NavigateTo(string address) { - Load(address); + control.Load(address); } public void ShowDeveloperConsole() { - GetBrowser().ShowDevTools(); + control.BrowserCore.ShowDevTools(); } public void Reload() { - GetBrowser().Reload(); + control.BrowserCore.Reload(); } public void Zoom(double level) { - GetBrowser().SetZoomLevel(level); - } - - private void BrowserControl_IsBrowserInitializedChanged(object sender, EventArgs e) - { - if (IsBrowserInitialized) - { - GetBrowser().GetHost().SetFocus(true); - } - } - - private void BrowserControl_LoadError(object sender, LoadErrorEventArgs e) - { - loadFailed?.Invoke((int) e.ErrorCode, e.ErrorText, e.FailedUrl); + control.BrowserCore.SetZoomLevel(level); } } } diff --git a/SafeExamBrowser.Browser/BrowserWindow.cs b/SafeExamBrowser.Browser/BrowserWindow.cs index 1585488e..e008f31c 100644 --- a/SafeExamBrowser.Browser/BrowserWindow.cs +++ b/SafeExamBrowser.Browser/BrowserWindow.cs @@ -7,9 +7,12 @@ */ 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; using SafeExamBrowser.Applications.Contracts.Events; using SafeExamBrowser.Browser.Contracts.Events; @@ -17,6 +20,7 @@ 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; @@ -31,6 +35,7 @@ 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; @@ -49,12 +54,12 @@ namespace SafeExamBrowser.Browser private readonly IKeyGenerator keyGenerator; private readonly IModuleLogger logger; private readonly IMessageBox messageBox; + private readonly Dictionary popups; private readonly BrowserSettings settings; private readonly string startUrl; private readonly IText text; private readonly IUserInterfaceFactory uiFactory; - private IBrowserControl control; private IBrowserWindow window; private double zoomLevel; @@ -63,6 +68,7 @@ namespace SafeExamBrowser.Browser get { return isMainWindow ? settings.MainWindow : settings.AdditionalWindow; } } + internal IBrowserControl Control { get; private set; } internal int Id { get; } public IntPtr Handle { get; private set; } @@ -81,31 +87,32 @@ namespace SafeExamBrowser.Browser public BrowserWindow( AppConfig appConfig, - BrowserSettings settings, - int id, - bool isMainWindow, IFileSystemDialog fileSystemDialog, IHashAlgorithm hashAlgorithm, + int id, + bool isMainWindow, IKeyGenerator keyGenerator, - IMessageBox messageBox, IModuleLogger logger, + IMessageBox messageBox, + BrowserSettings settings, + string startUrl, IText text, - IUserInterfaceFactory uiFactory, - string startUrl) + IUserInterfaceFactory uiFactory) { this.appConfig = appConfig; - this.Id = id; - this.httpClient = new HttpClient(); - this.isMainWindow = isMainWindow; this.fileSystemDialog = fileSystemDialog; this.hashAlgorithm = hashAlgorithm; + this.httpClient = new HttpClient(); + this.Id = id; + this.isMainWindow = isMainWindow; this.keyGenerator = keyGenerator; - this.messageBox = messageBox; this.logger = logger; + this.messageBox = messageBox; + this.popups = new Dictionary(); this.settings = settings; + this.startUrl = startUrl; this.text = text; this.uiFactory = uiFactory; - this.startUrl = startUrl; } public void Activate() @@ -116,31 +123,34 @@ namespace SafeExamBrowser.Browser internal void Close() { window.Close(); - control.Destroy(); + Control.Destroy(); } - internal void Initialize() - { - InitializeControl(); - InitializeWindow(); - } - - private void InitializeControl() + internal void InitializeControl() { + var cefSharpControl = default(ICefSharpControl); var contextMenuHandler = new ContextMenuHandler(); var dialogHandler = new DialogHandler(); var displayHandler = new DisplayHandler(); var downloadLogger = logger.CloneFor($"{nameof(DownloadHandler)} #{Id}"); var downloadHandler = new DownloadHandler(appConfig, downloadLogger, settings, WindowSettings); var keyboardHandler = new KeyboardHandler(); - var lifeSpanHandler = new LifeSpanHandler(); var requestFilter = new RequestFilter(); var requestLogger = logger.CloneFor($"{nameof(RequestHandler)} #{Id}"); var resourceHandler = new ResourceHandler(appConfig, requestFilter, keyGenerator, logger, settings, WindowSettings, text); - var requestHandler = new RequestHandler(appConfig, requestFilter, requestLogger, resourceHandler, 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; @@ -152,34 +162,65 @@ namespace SafeExamBrowser.Browser keyboardHandler.ZoomInRequested += ZoomInRequested; keyboardHandler.ZoomOutRequested += ZoomOutRequested; keyboardHandler.ZoomResetRequested += ZoomResetRequested; - lifeSpanHandler.PopupRequested += LifeSpanHandler_PopupRequested; resourceHandler.SessionIdentifierDetected += (id) => SessionIdentifierDetected?.Invoke(id); requestHandler.QuitUrlVisited += RequestHandler_QuitUrlVisited; requestHandler.RequestBlocked += RequestHandler_RequestBlocked; InitializeRequestFilter(requestFilter); - control = new BrowserControl( + Control = new BrowserControl( appConfig, - contextMenuHandler, + cefSharpControl, dialogHandler, displayHandler, downloadHandler, - keyGenerator, keyboardHandler, - lifeSpanHandler, + keyGenerator, requestHandler, - text, - startUrl); - control.AddressChanged += Control_AddressChanged; - control.LoadFailed += Control_LoadFailed; - control.LoadingStateChanged += Control_LoadingStateChanged; - control.TitleChanged += Control_TitleChanged; + text); + Control.AddressChanged += Control_AddressChanged; + Control.LoadFailed += Control_LoadFailed; + Control.LoadingStateChanged += Control_LoadingStateChanged; + Control.TitleChanged += Control_TitleChanged; - control.Initialize(); + Control.Initialize(); logger.Debug("Initialized browser control."); } + internal void InitializeWindow() + { + window = uiFactory.CreateBrowserWindow(Control, settings, isMainWindow); + 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; + 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) @@ -208,32 +249,9 @@ namespace SafeExamBrowser.Browser } } - private void InitializeWindow() - { - window = uiFactory.CreateBrowserWindow(control, settings, isMainWindow); - window.AddressChanged += Window_AddressChanged; - window.BackwardNavigationRequested += Window_BackwardNavigationRequested; - window.Closing += Window_Closing; - window.DeveloperConsoleRequested += Window_DeveloperConsoleRequested; - window.FindRequested += Window_FindRequested; - window.ForwardNavigationRequested += Window_ForwardNavigationRequested; - window.HomeNavigationRequested += HomeNavigationRequested; - 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 void Control_AddressChanged(string address) { - logger.Debug($"Navigated{(WindowSettings.UrlPolicy.CanLog() ? $" to '{address}'" : "")}."); + logger.Info($"Navigated{(WindowSettings.UrlPolicy.CanLog() ? $" to '{address}'" : "")}."); window.UpdateAddress(address); if (WindowSettings.UrlPolicy == UrlPolicy.Always || WindowSettings.UrlPolicy == UrlPolicy.BeforeTitle) @@ -265,14 +283,14 @@ namespace SafeExamBrowser.Browser logger.Warn($"Request{(WindowSettings.UrlPolicy.CanLogError() ? $" for '{url}'" : "")} failed: {errorText} ({errorCode})."); - Task.Run(() => messageBox.Show(message, title, icon: MessageBoxIcon.Error, parent: window)).ContinueWith(_ => control.NavigateBackwards()); + 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.CanNavigateBackwards = WindowSettings.AllowBackwardNavigation && Control.CanNavigateBackwards; + window.CanNavigateForwards = WindowSettings.AllowForwardNavigation && Control.CanNavigateForwards; window.UpdateLoadingState(isLoading); } @@ -423,7 +441,7 @@ namespace SafeExamBrowser.Browser if (navigate) { - control.NavigateTo(url); + Control.NavigateTo(url); } } } @@ -436,32 +454,67 @@ namespace SafeExamBrowser.Browser } } - private void LifeSpanHandler_PopupRequested(PopupRequestedEventArgs args) + private ChromiumHostControl LifeSpanHandler_CreatePopup() { - var validCurrentUri = Uri.TryCreate(control.Address, UriKind.Absolute, out var currentUri); - var validNewUri = Uri.TryCreate(args.Url, UriKind.Absolute, out var newUri); + 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 '{args.Url}'" : "")}..."); - PopupRequested?.Invoke(args); + 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() ? $" '{args.Url}'" : "")} directly..."); - control.NavigateTo(args.Url); + 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 '{args.Url}'" : "")} as it targets a different host."); + 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 '{args.Url}'" : "")}."); + logger.Info($"Blocked request to open new window{(WindowSettings.UrlPolicy.CanLog() ? $" for '{targetUrl}'" : "")}."); break; } + + return creation; } private void RequestHandler_QuitUrlVisited(string url) @@ -508,7 +561,7 @@ namespace SafeExamBrowser.Browser var message = text.Get(TextKey.MessageBox_BrowserNavigationBlocked).Replace("%%URL%%", WindowSettings.UrlPolicy.CanLogError() ? url : ""); var title = text.Get(TextKey.MessageBox_BrowserNavigationBlockedTitle); - control.TitleChanged -= Control_TitleChanged; + Control.TitleChanged -= Control_TitleChanged; if (url.Equals(startUrl, StringComparison.OrdinalIgnoreCase)) { @@ -517,7 +570,7 @@ namespace SafeExamBrowser.Browser } messageBox.Show(message, title, parent: window); - control.TitleChanged += Control_TitleChanged; + Control.TitleChanged += Control_TitleChanged; }); } @@ -530,7 +583,7 @@ namespace SafeExamBrowser.Browser if (result == MessageBoxResult.Yes) { logger.Debug("The user confirmed reloading the current page..."); - control.Reload(); + Control.Reload(); } else { @@ -540,7 +593,7 @@ namespace SafeExamBrowser.Browser else if (WindowSettings.AllowReloading) { logger.Debug("Reloading current page..."); - control.Reload(); + Control.Reload(); } else { @@ -555,7 +608,7 @@ namespace SafeExamBrowser.Browser if (isValid) { logger.Debug($"The user requested to navigate to '{address}', the URI is valid."); - control.NavigateTo(address); + Control.NavigateTo(address); } else { @@ -567,34 +620,39 @@ namespace SafeExamBrowser.Browser private void Window_BackwardNavigationRequested() { logger.Debug("Navigating backwards..."); - control.NavigateBackwards(); + Control.NavigateBackwards(); } private void Window_Closing() { - logger.Info($"Window is closing..."); - control.Destroy(); + 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(); + Control.ShowDeveloperConsole(); } private void Window_FindRequested(string term, bool isInitial, bool caseSensitive, bool forward = true) { if (settings.AllowFind) { - control.Find(term, isInitial, caseSensitive, forward); + Control.Find(term, isInitial, caseSensitive, forward); } } private void Window_ForwardNavigationRequested() { logger.Debug("Navigating forwards..."); - control.NavigateForwards(); + Control.NavigateForwards(); } private void ZoomInRequested() @@ -602,7 +660,7 @@ namespace SafeExamBrowser.Browser if (settings.AllowPageZoom && CalculateZoomPercentage() < 300) { zoomLevel += ZOOM_FACTOR; - control.Zoom(zoomLevel); + Control.Zoom(zoomLevel); window.UpdateZoomLevel(CalculateZoomPercentage()); logger.Debug($"Increased page zoom to {CalculateZoomPercentage()}%."); } @@ -613,7 +671,7 @@ namespace SafeExamBrowser.Browser if (settings.AllowPageZoom && CalculateZoomPercentage() > 25) { zoomLevel -= ZOOM_FACTOR; - control.Zoom(zoomLevel); + Control.Zoom(zoomLevel); window.UpdateZoomLevel(CalculateZoomPercentage()); logger.Debug($"Decreased page zoom to {CalculateZoomPercentage()}%."); } @@ -624,7 +682,7 @@ namespace SafeExamBrowser.Browser if (settings.AllowPageZoom) { zoomLevel = 0; - control.Zoom(0); + Control.Zoom(0); window.UpdateZoomLevel(CalculateZoomPercentage()); logger.Debug($"Reset page zoom to {CalculateZoomPercentage()}%."); } diff --git a/SafeExamBrowser.Browser/Events/PopupRequestedEventArgs.cs b/SafeExamBrowser.Browser/Events/PopupRequestedEventArgs.cs index 5b0f4d79..e1cc9fc9 100644 --- a/SafeExamBrowser.Browser/Events/PopupRequestedEventArgs.cs +++ b/SafeExamBrowser.Browser/Events/PopupRequestedEventArgs.cs @@ -10,6 +10,6 @@ namespace SafeExamBrowser.Browser.Events { internal class PopupRequestedEventArgs { - public string Url { get; set; } + public BrowserWindow Window { get; set; } } } diff --git a/SafeExamBrowser.Browser/Handlers/DialogHandler.cs b/SafeExamBrowser.Browser/Handlers/DialogHandler.cs index 8f09db7f..2e742976 100644 --- a/SafeExamBrowser.Browser/Handlers/DialogHandler.cs +++ b/SafeExamBrowser.Browser/Handlers/DialogHandler.cs @@ -10,7 +10,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using CefSharp; using SafeExamBrowser.Browser.Events; -using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog; +using SafeExamBrowser.Browser.Wrapper; namespace SafeExamBrowser.Browser.Handlers { @@ -22,9 +22,9 @@ namespace SafeExamBrowser.Browser.Handlers { var args = new DialogRequestedEventArgs { - Element = ToElement(mode), + Element = mode.ToElement(), InitialPath = defaultFilePath, - Operation = ToOperation(mode), + Operation = mode.ToOperation(), Title = title }; @@ -47,27 +47,5 @@ namespace SafeExamBrowser.Browser.Handlers return true; } - - private FileSystemElement ToElement(CefFileDialogMode mode) - { - switch (mode) - { - case CefFileDialogMode.OpenFolder: - return FileSystemElement.Folder; - default: - return FileSystemElement.File; - } - } - - private FileSystemOperation ToOperation(CefFileDialogMode mode) - { - switch (mode) - { - case CefFileDialogMode.Save: - return FileSystemOperation.Save; - default: - return FileSystemOperation.Open; - } - } } } diff --git a/SafeExamBrowser.Browser/Handlers/DownloadHandler.cs b/SafeExamBrowser.Browser/Handlers/DownloadHandler.cs index 96d45ebd..22f265e1 100644 --- a/SafeExamBrowser.Browser/Handlers/DownloadHandler.cs +++ b/SafeExamBrowser.Browser/Handlers/DownloadHandler.cs @@ -24,12 +24,12 @@ namespace SafeExamBrowser.Browser.Handlers { internal class DownloadHandler : IDownloadHandler { - private AppConfig appConfig; - private BrowserSettings settings; - private WindowSettings windowSettings; - private ConcurrentDictionary callbacks; - private ConcurrentDictionary downloads; - private ILogger logger; + private readonly AppConfig appConfig; + private readonly ConcurrentDictionary callbacks; + private readonly ConcurrentDictionary downloads; + private readonly ILogger logger; + private readonly BrowserSettings settings; + private readonly WindowSettings windowSettings; internal event DownloadRequestedEventHandler ConfigurationDownloadRequested; internal event DownloadUpdatedEventHandler DownloadUpdated; @@ -85,7 +85,7 @@ namespace SafeExamBrowser.Browser.Handlers IsComplete = downloadItem.IsComplete, Url = downloadItem.Url }; - + Task.Run(() => DownloadUpdated?.Invoke(state)); } diff --git a/SafeExamBrowser.Browser/Handlers/LifeSpanHandler.cs b/SafeExamBrowser.Browser/Handlers/LifeSpanHandler.cs deleted file mode 100644 index ecf844f2..00000000 --- a/SafeExamBrowser.Browser/Handlers/LifeSpanHandler.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) - * - * 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 CefSharp; -using SafeExamBrowser.Browser.Events; - -namespace SafeExamBrowser.Browser.Handlers -{ - internal class LifeSpanHandler : ILifeSpanHandler - { - public event PopupRequestedEventHandler PopupRequested; - - public bool DoClose(IWebBrowser chromiumWebBrowser, IBrowser browser) - { - return false; - } - - public void OnAfterCreated(IWebBrowser chromiumWebBrowser, IBrowser browser) - { - } - - public void OnBeforeClose(IWebBrowser chromiumWebBrowser, IBrowser browser) - { - } - - public bool OnBeforePopup(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser) - { - var args = new PopupRequestedEventArgs { Url = targetUrl }; - - newBrowser = default(IWebBrowser); - PopupRequested?.Invoke(args); - - return true; - } - } -} diff --git a/SafeExamBrowser.Browser/Handlers/RequestHandler.cs b/SafeExamBrowser.Browser/Handlers/RequestHandler.cs index 189350fa..5ec0b472 100644 --- a/SafeExamBrowser.Browser/Handlers/RequestHandler.cs +++ b/SafeExamBrowser.Browser/Handlers/RequestHandler.cs @@ -14,7 +14,6 @@ using CefSharp; using SafeExamBrowser.Browser.Contracts.Filters; using SafeExamBrowser.Browser.Events; using SafeExamBrowser.Configuration.Contracts; -using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Settings.Browser; using SafeExamBrowser.Settings.Browser.Filter; @@ -25,13 +24,14 @@ namespace SafeExamBrowser.Browser.Handlers { internal class RequestHandler : CefSharp.Handler.RequestHandler { - private AppConfig appConfig; - private IRequestFilter filter; - private ILogger logger; + private readonly AppConfig appConfig; + private readonly IRequestFilter filter; + private readonly ILogger logger; + private readonly ResourceHandler resourceHandler; + private readonly WindowSettings windowSettings; + private readonly BrowserSettings settings; + private string quitUrlPattern; - private ResourceHandler resourceHandler; - private WindowSettings windowSettings; - private BrowserSettings settings; internal event UrlEventHandler QuitUrlVisited; internal event UrlEventHandler RequestBlocked; @@ -42,8 +42,7 @@ namespace SafeExamBrowser.Browser.Handlers ILogger logger, ResourceHandler resourceHandler, BrowserSettings settings, - WindowSettings windowSettings, - IText text) + WindowSettings windowSettings) { this.appConfig = appConfig; this.filter = filter; @@ -53,16 +52,7 @@ namespace SafeExamBrowser.Browser.Handlers this.windowSettings = windowSettings; } - protected override bool GetAuthCredentials( - IWebBrowser webBrowser, - IBrowser browser, - string originUrl, - bool isProxy, - string host, - int port, - string realm, - string scheme, - IAuthCallback callback) + protected override bool GetAuthCredentials(IWebBrowser webBrowser, IBrowser browser, string originUrl, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback) { if (isProxy) { @@ -80,15 +70,7 @@ namespace SafeExamBrowser.Browser.Handlers return base.GetAuthCredentials(webBrowser, browser, originUrl, isProxy, host, port, realm, scheme, callback); } - protected override IResourceRequestHandler GetResourceRequestHandler( - IWebBrowser webBrowser, - IBrowser browser, - IFrame frame, - IRequest request, - bool isNavigation, - bool isDownload, - string requestInitiator, - ref bool disableDefaultHandling) + protected override IResourceRequestHandler GetResourceRequestHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling) { return resourceHandler; } @@ -122,13 +104,7 @@ namespace SafeExamBrowser.Browser.Handlers return base.OnBeforeBrowse(webBrowser, browser, frame, request, userGesture, isRedirect); } - protected override bool OnOpenUrlFromTab( - IWebBrowser webBrowser, - IBrowser browser, - IFrame frame, - string targetUrl, - WindowOpenDisposition targetDisposition, - bool userGesture) + protected override bool OnOpenUrlFromTab(IWebBrowser webBrowser, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture) { switch (targetDisposition) { @@ -172,7 +148,7 @@ namespace SafeExamBrowser.Browser.Handlers if (!string.IsNullOrWhiteSpace(settings.QuitUrl)) { - if (quitUrlPattern == default(string)) + if (quitUrlPattern == default) { quitUrlPattern = Regex.Escape(settings.QuitUrl.TrimEnd('/')) + @"\/?"; } diff --git a/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj b/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj index 9b24e49e..808d2a3b 100644 --- a/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj +++ b/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj @@ -1,8 +1,8 @@  - - - + + + Debug @@ -53,14 +53,14 @@ prompt - - ..\packages\CefSharp.Common.97.1.11\lib\net452\CefSharp.dll + + ..\packages\CefSharp.Common.98.1.210\lib\net452\CefSharp.dll - - ..\packages\CefSharp.Common.97.1.11\lib\net452\CefSharp.Core.dll + + ..\packages\CefSharp.Common.98.1.210\lib\net452\CefSharp.Core.dll - - ..\packages\CefSharp.WinForms.97.1.11\lib\net462\CefSharp.WinForms.dll + + ..\packages\CefSharp.WinForms.98.1.210\lib\net462\CefSharp.WinForms.dll ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll @@ -95,19 +95,42 @@ - - Component - + - + + Component + + + Component + + + + + + + + + + + + + + + + + + + + + @@ -153,9 +176,7 @@ - - Designer - + @@ -172,10 +193,10 @@ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - + + + + - + \ No newline at end of file diff --git a/SafeExamBrowser.Browser/Wrapper/CefSharpBrowserControl.cs b/SafeExamBrowser.Browser/Wrapper/CefSharpBrowserControl.cs new file mode 100644 index 00000000..6f7deec7 --- /dev/null +++ b/SafeExamBrowser.Browser/Wrapper/CefSharpBrowserControl.cs @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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.Collections.Generic; +using CefSharp; +using CefSharp.WinForms; +using SafeExamBrowser.Browser.Handlers; +using SafeExamBrowser.Browser.Wrapper.Events; +using SafeExamBrowser.Browser.Wrapper.Handlers; + +namespace SafeExamBrowser.Browser.Wrapper +{ + internal class CefSharpBrowserControl : ChromiumWebBrowser, ICefSharpControl + { + public event AuthCredentialsEventHandler AuthCredentialsRequired; + public event BeforeBrowseEventHandler BeforeBrowse; + public event BeforeDownloadEventHandler BeforeDownload; + public event DownloadUpdatedEventHandler DownloadUpdated; + public event FaviconUrlChangedEventHandler FaviconUrlChanged; + public event FileDialogRequestedEventHandler FileDialogRequested; + public event KeyEventHandler KeyEvent; + public event LoadingProgressChangedEventHandler LoadingProgressChanged; + public event OpenUrlFromTabEventHandler OpenUrlFromTab; + public event PreKeyEventHandler PreKeyEvent; + public event ResourceRequestEventHandler ResourceRequestHandlerRequired; + + public CefSharpBrowserControl(ILifeSpanHandler lifeSpanHandler, string url) : base(url) + { + DialogHandler = new DialogHandlerSwitch(); + DisplayHandler = new DisplayHandlerSwitch(); + DownloadHandler = new DownloadHandlerSwitch(); + KeyboardHandler = new KeyboardHandlerSwitch(); + LifeSpanHandler = lifeSpanHandler; + MenuHandler = new ContextMenuHandler(); + RequestHandler = new RequestHandlerSwitch(); + } + + void ICefSharpControl.Dispose(bool disposing) + { + base.Dispose(disposing); + } + + public void GetAuthCredentials(IWebBrowser webBrowser, IBrowser browser, string originUrl, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback, GenericEventArgs args) + { + AuthCredentialsRequired?.Invoke(webBrowser, browser, originUrl, isProxy, host, port, realm, scheme, callback, args); + } + + public void GetResourceRequestHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling, ResourceRequestEventArgs args) + { + ResourceRequestHandlerRequired?.Invoke(webBrowser, browser, frame, request, isNavigation, isDownload, requestInitiator, ref disableDefaultHandling, args); + } + + public void OnBeforeBrowse(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, bool userGesture, bool isRedirect, GenericEventArgs args) + { + BeforeBrowse?.Invoke(webBrowser, browser, frame, request, userGesture, isRedirect, args); + } + + public void OnBeforeDownload(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback) + { + BeforeDownload?.Invoke(webBrowser, browser, downloadItem, callback); + } + + public void OnDownloadUpdated(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback) + { + DownloadUpdated?.Invoke(webBrowser, browser, downloadItem, callback); + } + + public void OnFaviconUrlChange(IWebBrowser webBrowser, IBrowser browser, IList urls) + { + FaviconUrlChanged?.Invoke(webBrowser, browser, urls); + } + + public void OnFileDialog(IWebBrowser webBrowser, IBrowser browser, CefFileDialogMode mode, CefFileDialogFlags flags, string title, string defaultFilePath, List acceptFilters, int selectedAcceptFilter, IFileDialogCallback callback) + { + FileDialogRequested?.Invoke(webBrowser, browser, mode, flags, title, defaultFilePath, acceptFilters, selectedAcceptFilter, callback); + } + + public void OnKeyEvent(IWebBrowser webBrowser, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey) + { + KeyEvent?.Invoke(webBrowser, browser, type, windowsKeyCode, nativeKeyCode, modifiers, isSystemKey); + } + + public void OnLoadingProgressChange(IWebBrowser webBrowser, IBrowser browser, double progress) + { + LoadingProgressChanged?.Invoke(webBrowser, browser, progress); + } + + public void OnOpenUrlFromTab(IWebBrowser webBrowser, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture, GenericEventArgs args) + { + OpenUrlFromTab?.Invoke(webBrowser, browser, frame, targetUrl, targetDisposition, userGesture, args); + } + + public void OnPreKeyEvent(IWebBrowser webBrowser, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut, GenericEventArgs args) + { + PreKeyEvent?.Invoke(webBrowser, browser, type, windowsKeyCode, nativeKeyCode, modifiers, isSystemKey, ref isKeyboardShortcut, args); + } + } +} diff --git a/SafeExamBrowser.Browser/Wrapper/CefSharpPopupControl.cs b/SafeExamBrowser.Browser/Wrapper/CefSharpPopupControl.cs new file mode 100644 index 00000000..50227ce2 --- /dev/null +++ b/SafeExamBrowser.Browser/Wrapper/CefSharpPopupControl.cs @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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.Collections.Generic; +using CefSharp; +using CefSharp.WinForms.Host; +using SafeExamBrowser.Browser.Wrapper.Events; + +namespace SafeExamBrowser.Browser.Wrapper +{ + internal class CefSharpPopupControl : ChromiumHostControl, ICefSharpControl + { + public event AuthCredentialsEventHandler AuthCredentialsRequired; + public event BeforeBrowseEventHandler BeforeBrowse; + public event BeforeDownloadEventHandler BeforeDownload; + public event DownloadUpdatedEventHandler DownloadUpdated; + public event FaviconUrlChangedEventHandler FaviconUrlChanged; + public event FileDialogRequestedEventHandler FileDialogRequested; + public event KeyEventHandler KeyEvent; + public event LoadingProgressChangedEventHandler LoadingProgressChanged; + public event OpenUrlFromTabEventHandler OpenUrlFromTab; + public event PreKeyEventHandler PreKeyEvent; + public event ResourceRequestEventHandler ResourceRequestHandlerRequired; + + void ICefSharpControl.Dispose(bool disposing) + { + if (!IsDisposed && IsHandleCreated) + { + base.Dispose(disposing); + } + } + + public void GetAuthCredentials(IWebBrowser webBrowser, IBrowser browser, string originUrl, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback, GenericEventArgs args) + { + AuthCredentialsRequired?.Invoke(webBrowser, browser, originUrl, isProxy, host, port, realm, scheme, callback, args); + } + + public void GetResourceRequestHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling, ResourceRequestEventArgs args) + { + ResourceRequestHandlerRequired?.Invoke(webBrowser, browser, frame, request, isNavigation, isDownload, requestInitiator, ref disableDefaultHandling, args); + } + + public void Load(string address) + { + LoadUrl(address); + } + + public void OnBeforeBrowse(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, bool userGesture, bool isRedirect, GenericEventArgs args) + { + BeforeBrowse?.Invoke(webBrowser, browser, frame, request, userGesture, isRedirect, args); + } + + public void OnBeforeDownload(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback) + { + BeforeDownload?.Invoke(webBrowser, browser, downloadItem, callback); + } + + public void OnDownloadUpdated(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback) + { + DownloadUpdated?.Invoke(webBrowser, browser, downloadItem, callback); + } + + public void OnFaviconUrlChange(IWebBrowser webBrowser, IBrowser browser, IList urls) + { + FaviconUrlChanged?.Invoke(webBrowser, browser, urls); + } + + public void OnFileDialog(IWebBrowser webBrowser, IBrowser browser, CefFileDialogMode mode, CefFileDialogFlags flags, string title, string defaultFilePath, List acceptFilters, int selectedAcceptFilter, IFileDialogCallback callback) + { + FileDialogRequested?.Invoke(webBrowser, browser, mode, flags, title, defaultFilePath, acceptFilters, selectedAcceptFilter, callback); + } + + public void OnKeyEvent(IWebBrowser webBrowser, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey) + { + KeyEvent?.Invoke(webBrowser, browser, type, windowsKeyCode, nativeKeyCode, modifiers, isSystemKey); + } + + public void OnLoadingProgressChange(IWebBrowser webBrowser, IBrowser browser, double progress) + { + LoadingProgressChanged?.Invoke(webBrowser, browser, progress); + } + + public void OnOpenUrlFromTab(IWebBrowser webBrowser, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture, GenericEventArgs args) + { + OpenUrlFromTab?.Invoke(webBrowser, browser, frame, targetUrl, targetDisposition, userGesture, args); + } + + public void OnPreKeyEvent(IWebBrowser webBrowser, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut, GenericEventArgs args) + { + PreKeyEvent?.Invoke(webBrowser, browser, type, windowsKeyCode, nativeKeyCode, modifiers, isSystemKey, ref isKeyboardShortcut, args); + } + } +} diff --git a/SafeExamBrowser.Browser/Wrapper/Events/AuthCredentialsEventHandler.cs b/SafeExamBrowser.Browser/Wrapper/Events/AuthCredentialsEventHandler.cs new file mode 100644 index 00000000..d5c730f6 --- /dev/null +++ b/SafeExamBrowser.Browser/Wrapper/Events/AuthCredentialsEventHandler.cs @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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 CefSharp; + +namespace SafeExamBrowser.Browser.Wrapper.Events +{ + internal delegate void AuthCredentialsEventHandler(IWebBrowser webBrowser, IBrowser browser, string originUrl, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback, GenericEventArgs args); +} diff --git a/SafeExamBrowser.Browser/Wrapper/Events/BeforeBrowseEventHandler.cs b/SafeExamBrowser.Browser/Wrapper/Events/BeforeBrowseEventHandler.cs new file mode 100644 index 00000000..66b6282c --- /dev/null +++ b/SafeExamBrowser.Browser/Wrapper/Events/BeforeBrowseEventHandler.cs @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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 CefSharp; + +namespace SafeExamBrowser.Browser.Wrapper.Events +{ + internal delegate void BeforeBrowseEventHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, bool userGesture, bool isRedirect, GenericEventArgs args); +} diff --git a/SafeExamBrowser.Browser/Wrapper/Events/BeforeDownloadEventHandler.cs b/SafeExamBrowser.Browser/Wrapper/Events/BeforeDownloadEventHandler.cs new file mode 100644 index 00000000..5d8f8d8e --- /dev/null +++ b/SafeExamBrowser.Browser/Wrapper/Events/BeforeDownloadEventHandler.cs @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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 CefSharp; + +namespace SafeExamBrowser.Browser.Wrapper.Events +{ + internal delegate void BeforeDownloadEventHandler(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback); +} diff --git a/SafeExamBrowser.Browser/Wrapper/Events/DownloadUpdatedEventHandler.cs b/SafeExamBrowser.Browser/Wrapper/Events/DownloadUpdatedEventHandler.cs new file mode 100644 index 00000000..4a8fa698 --- /dev/null +++ b/SafeExamBrowser.Browser/Wrapper/Events/DownloadUpdatedEventHandler.cs @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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 CefSharp; + +namespace SafeExamBrowser.Browser.Wrapper.Events +{ + internal delegate void DownloadUpdatedEventHandler(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback); +} diff --git a/SafeExamBrowser.Browser/Wrapper/Events/FaviconUrlChangedEventHandler.cs b/SafeExamBrowser.Browser/Wrapper/Events/FaviconUrlChangedEventHandler.cs new file mode 100644 index 00000000..75a00a4a --- /dev/null +++ b/SafeExamBrowser.Browser/Wrapper/Events/FaviconUrlChangedEventHandler.cs @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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.Collections.Generic; +using CefSharp; + +namespace SafeExamBrowser.Browser.Wrapper.Events +{ + internal delegate void FaviconUrlChangedEventHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IList urls); +} diff --git a/SafeExamBrowser.Browser/Wrapper/Events/FileDialogRequestedEventHandler.cs b/SafeExamBrowser.Browser/Wrapper/Events/FileDialogRequestedEventHandler.cs new file mode 100644 index 00000000..89c9d851 --- /dev/null +++ b/SafeExamBrowser.Browser/Wrapper/Events/FileDialogRequestedEventHandler.cs @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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.Collections.Generic; +using CefSharp; + +namespace SafeExamBrowser.Browser.Wrapper.Events +{ + internal delegate void FileDialogRequestedEventHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, CefFileDialogMode mode, CefFileDialogFlags flags, string title, string defaultFilePath, List acceptFilters, int selectedAcceptFilter, IFileDialogCallback callback); +} diff --git a/SafeExamBrowser.Browser/Wrapper/Events/GenericEventArgs.cs b/SafeExamBrowser.Browser/Wrapper/Events/GenericEventArgs.cs new file mode 100644 index 00000000..a81b7cba --- /dev/null +++ b/SafeExamBrowser.Browser/Wrapper/Events/GenericEventArgs.cs @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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/. + */ + +namespace SafeExamBrowser.Browser.Wrapper.Events +{ + internal class GenericEventArgs + { + public bool Value { get; set; } + } +} diff --git a/SafeExamBrowser.Browser/Wrapper/Events/KeyEventHandler.cs b/SafeExamBrowser.Browser/Wrapper/Events/KeyEventHandler.cs new file mode 100644 index 00000000..e1041129 --- /dev/null +++ b/SafeExamBrowser.Browser/Wrapper/Events/KeyEventHandler.cs @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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 CefSharp; + +namespace SafeExamBrowser.Browser.Wrapper.Events +{ + internal delegate void KeyEventHandler(IWebBrowser webBrowser, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey); +} diff --git a/SafeExamBrowser.Browser/Wrapper/Events/LoadingProgressChangedEventHandler.cs b/SafeExamBrowser.Browser/Wrapper/Events/LoadingProgressChangedEventHandler.cs new file mode 100644 index 00000000..7cdf4d33 --- /dev/null +++ b/SafeExamBrowser.Browser/Wrapper/Events/LoadingProgressChangedEventHandler.cs @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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 CefSharp; + +namespace SafeExamBrowser.Browser.Wrapper.Events +{ + internal delegate void LoadingProgressChangedEventHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, double progress); +} diff --git a/SafeExamBrowser.Browser/Wrapper/Events/OpenUrlFromTabEventHandler.cs b/SafeExamBrowser.Browser/Wrapper/Events/OpenUrlFromTabEventHandler.cs new file mode 100644 index 00000000..37ced062 --- /dev/null +++ b/SafeExamBrowser.Browser/Wrapper/Events/OpenUrlFromTabEventHandler.cs @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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 CefSharp; + +namespace SafeExamBrowser.Browser.Wrapper.Events +{ + internal delegate void OpenUrlFromTabEventHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture, GenericEventArgs args); +} diff --git a/SafeExamBrowser.Browser/Wrapper/Events/PreKeyEventHandler.cs b/SafeExamBrowser.Browser/Wrapper/Events/PreKeyEventHandler.cs new file mode 100644 index 00000000..4df7f70c --- /dev/null +++ b/SafeExamBrowser.Browser/Wrapper/Events/PreKeyEventHandler.cs @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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 CefSharp; + +namespace SafeExamBrowser.Browser.Wrapper.Events +{ + internal delegate void PreKeyEventHandler(IWebBrowser webBrowser, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut, GenericEventArgs args); +} diff --git a/SafeExamBrowser.Browser/Wrapper/Events/ResourceRequestEventArgs.cs b/SafeExamBrowser.Browser/Wrapper/Events/ResourceRequestEventArgs.cs new file mode 100644 index 00000000..b876a835 --- /dev/null +++ b/SafeExamBrowser.Browser/Wrapper/Events/ResourceRequestEventArgs.cs @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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 CefSharp; + +namespace SafeExamBrowser.Browser.Wrapper.Events +{ + internal class ResourceRequestEventArgs + { + public IResourceRequestHandler Handler { get; set; } + } +} diff --git a/SafeExamBrowser.Browser/Wrapper/Events/ResourceRequestEventHandler.cs b/SafeExamBrowser.Browser/Wrapper/Events/ResourceRequestEventHandler.cs new file mode 100644 index 00000000..6747ca0e --- /dev/null +++ b/SafeExamBrowser.Browser/Wrapper/Events/ResourceRequestEventHandler.cs @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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 CefSharp; + +namespace SafeExamBrowser.Browser.Wrapper.Events +{ + internal delegate void ResourceRequestEventHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling, ResourceRequestEventArgs args); +} diff --git a/SafeExamBrowser.Browser/Wrapper/Extensions.cs b/SafeExamBrowser.Browser/Wrapper/Extensions.cs new file mode 100644 index 00000000..ca7de6a5 --- /dev/null +++ b/SafeExamBrowser.Browser/Wrapper/Extensions.cs @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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 CefSharp; +using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog; + +namespace SafeExamBrowser.Browser.Wrapper +{ + internal static class Extensions + { + internal static FileSystemElement ToElement(this CefFileDialogMode mode) + { + switch (mode) + { + case CefFileDialogMode.OpenFolder: + return FileSystemElement.Folder; + default: + return FileSystemElement.File; + } + } + + internal static FileSystemOperation ToOperation(this CefFileDialogMode mode) + { + switch (mode) + { + case CefFileDialogMode.Save: + return FileSystemOperation.Save; + default: + return FileSystemOperation.Open; + } + } + } +} diff --git a/SafeExamBrowser.Browser/Wrapper/Handlers/DialogHandlerSwitch.cs b/SafeExamBrowser.Browser/Wrapper/Handlers/DialogHandlerSwitch.cs new file mode 100644 index 00000000..710b6512 --- /dev/null +++ b/SafeExamBrowser.Browser/Wrapper/Handlers/DialogHandlerSwitch.cs @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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.Collections.Generic; +using CefSharp; +using CefSharp.WinForms; +using CefSharp.WinForms.Host; + +namespace SafeExamBrowser.Browser.Wrapper.Handlers +{ + internal class DialogHandlerSwitch : IDialogHandler + { + public bool OnFileDialog(IWebBrowser webBrowser, IBrowser browser, CefFileDialogMode mode, CefFileDialogFlags flags, string title, string defaultFilePath, List acceptFilters, int selectedAcceptFilter, IFileDialogCallback callback) + { + if (browser.IsPopup) + { + var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl; + + control?.OnFileDialog(webBrowser, browser, mode, flags, title, defaultFilePath, acceptFilters, selectedAcceptFilter, callback); + } + else + { + var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl; + + control?.OnFileDialog(webBrowser, browser, mode, flags, title, defaultFilePath, acceptFilters, selectedAcceptFilter, callback); + } + + return true; + } + } +} diff --git a/SafeExamBrowser.Browser/Wrapper/Handlers/DisplayHandlerSwitch.cs b/SafeExamBrowser.Browser/Wrapper/Handlers/DisplayHandlerSwitch.cs new file mode 100644 index 00000000..0cffb92b --- /dev/null +++ b/SafeExamBrowser.Browser/Wrapper/Handlers/DisplayHandlerSwitch.cs @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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.Collections.Generic; +using CefSharp; +using CefSharp.WinForms; +using CefSharp.WinForms.Handler; +using CefSharp.WinForms.Host; + +namespace SafeExamBrowser.Browser.Wrapper.Handlers +{ + internal class DisplayHandlerSwitch : DisplayHandler + { + protected override void OnFaviconUrlChange(IWebBrowser chromiumWebBrowser, IBrowser browser, IList urls) + { + if (browser.IsPopup) + { + var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl; + + control?.OnFaviconUrlChange(chromiumWebBrowser, browser, urls); + } + else + { + var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl; + + control?.OnFaviconUrlChange(chromiumWebBrowser, browser, urls); + } + + base.OnFaviconUrlChange(chromiumWebBrowser, browser, urls); + } + + protected override void OnLoadingProgressChange(IWebBrowser chromiumWebBrowser, IBrowser browser, double progress) + { + if (browser.IsPopup) + { + var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl; + + control?.OnLoadingProgressChange(chromiumWebBrowser, browser, progress); + } + else + { + var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl; + + control?.OnLoadingProgressChange(chromiumWebBrowser, browser, progress); + } + + base.OnLoadingProgressChange(chromiumWebBrowser, browser, progress); + } + } +} diff --git a/SafeExamBrowser.Browser/Wrapper/Handlers/DownloadHandlerSwitch.cs b/SafeExamBrowser.Browser/Wrapper/Handlers/DownloadHandlerSwitch.cs new file mode 100644 index 00000000..43f29c44 --- /dev/null +++ b/SafeExamBrowser.Browser/Wrapper/Handlers/DownloadHandlerSwitch.cs @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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 CefSharp; +using CefSharp.WinForms; +using CefSharp.WinForms.Host; + +namespace SafeExamBrowser.Browser.Wrapper.Handlers +{ + internal class DownloadHandlerSwitch : IDownloadHandler + { + public void OnBeforeDownload(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback) + { + if (browser.IsPopup) + { + var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl; + + control?.OnBeforeDownload(webBrowser, browser, downloadItem, callback); + } + else + { + var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl; + + control?.OnBeforeDownload(webBrowser, browser, downloadItem, callback); + } + } + + public void OnDownloadUpdated(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback) + { + if (browser.IsPopup) + { + var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl; + + control?.OnDownloadUpdated(webBrowser, browser, downloadItem, callback); + } + else + { + var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl; + + control?.OnDownloadUpdated(webBrowser, browser, downloadItem, callback); + } + } + } +} diff --git a/SafeExamBrowser.Browser/Wrapper/Handlers/KeyboardHandlerSwitch.cs b/SafeExamBrowser.Browser/Wrapper/Handlers/KeyboardHandlerSwitch.cs new file mode 100644 index 00000000..a88ebff6 --- /dev/null +++ b/SafeExamBrowser.Browser/Wrapper/Handlers/KeyboardHandlerSwitch.cs @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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 CefSharp; +using CefSharp.WinForms; +using CefSharp.WinForms.Host; +using SafeExamBrowser.Browser.Wrapper.Events; + +namespace SafeExamBrowser.Browser.Wrapper.Handlers +{ + internal class KeyboardHandlerSwitch : IKeyboardHandler + { + public bool OnKeyEvent(IWebBrowser webBrowser, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey) + { + if (browser.IsPopup) + { + var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl; + + control?.OnKeyEvent(webBrowser, browser, type, windowsKeyCode, nativeKeyCode, modifiers, isSystemKey); + } + else + { + var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl; + + control?.OnKeyEvent(webBrowser, browser, type, windowsKeyCode, nativeKeyCode, modifiers, isSystemKey); + } + + return false; + } + + public bool OnPreKeyEvent(IWebBrowser webBrowser, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut) + { + var args = new GenericEventArgs { Value = false }; + + if (browser.IsPopup) + { + var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl; + + control?.OnPreKeyEvent(webBrowser, browser, type, windowsKeyCode, nativeKeyCode, modifiers, isSystemKey, ref isKeyboardShortcut, args); + } + else + { + var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl; + + control?.OnPreKeyEvent(webBrowser, browser, type, windowsKeyCode, nativeKeyCode, modifiers, isSystemKey, ref isKeyboardShortcut, args); + } + + return args.Value; + } + } +} diff --git a/SafeExamBrowser.Browser/Wrapper/Handlers/RequestHandlerSwitch.cs b/SafeExamBrowser.Browser/Wrapper/Handlers/RequestHandlerSwitch.cs new file mode 100644 index 00000000..288e9f88 --- /dev/null +++ b/SafeExamBrowser.Browser/Wrapper/Handlers/RequestHandlerSwitch.cs @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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 CefSharp; +using CefSharp.Handler; +using CefSharp.WinForms; +using CefSharp.WinForms.Host; +using SafeExamBrowser.Browser.Wrapper.Events; + +namespace SafeExamBrowser.Browser.Wrapper.Handlers +{ + internal class RequestHandlerSwitch : RequestHandler + { + protected override bool GetAuthCredentials(IWebBrowser webBrowser, IBrowser browser, string originUrl, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback) + { + var args = new GenericEventArgs { Value = false }; + + if (browser.IsPopup) + { + var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl; + + control?.GetAuthCredentials(webBrowser, browser, originUrl, isProxy, host, port, realm, scheme, callback, args); + } + else + { + var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl; + + control?.GetAuthCredentials(webBrowser, browser, originUrl, isProxy, host, port, realm, scheme, callback, args); + } + + return args.Value; + } + + protected override IResourceRequestHandler GetResourceRequestHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling) + { + var args = new ResourceRequestEventArgs(); + + if (browser.IsPopup) + { + var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl; + + control?.GetResourceRequestHandler(chromiumWebBrowser, browser, frame, request, isNavigation, isDownload, requestInitiator, ref disableDefaultHandling, args); + } + else + { + var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl; + + control?.GetResourceRequestHandler(chromiumWebBrowser, browser, frame, request, isNavigation, isDownload, requestInitiator, ref disableDefaultHandling, args); + } + + return args.Handler; + } + + protected override bool OnBeforeBrowse(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool userGesture, bool isRedirect) + { + var args = new GenericEventArgs { Value = false }; + + if (browser.IsPopup) + { + var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl; + + control?.OnBeforeBrowse(chromiumWebBrowser, browser, frame, request, userGesture, isRedirect, args); + } + else + { + var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl; + + control?.OnBeforeBrowse(chromiumWebBrowser, browser, frame, request, userGesture, isRedirect, args); + } + + return args.Value; + } + + protected override bool OnOpenUrlFromTab(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture) + { + var args = new GenericEventArgs { Value = false }; + + if (browser.IsPopup) + { + var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl; + + control?.OnOpenUrlFromTab(chromiumWebBrowser, browser, frame, targetUrl, targetDisposition, userGesture, args); + } + else + { + var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl; + + control?.OnOpenUrlFromTab(chromiumWebBrowser, browser, frame, targetUrl, targetDisposition, userGesture, args); + } + + return args.Value; + } + } +} diff --git a/SafeExamBrowser.Browser/Wrapper/ICefSharpControl.cs b/SafeExamBrowser.Browser/Wrapper/ICefSharpControl.cs new file mode 100644 index 00000000..dc14a499 --- /dev/null +++ b/SafeExamBrowser.Browser/Wrapper/ICefSharpControl.cs @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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.Collections.Generic; +using System.ComponentModel; +using System.Windows.Forms; +using CefSharp; +using CefSharp.WinForms; +using SafeExamBrowser.Browser.Wrapper.Events; +using KeyEventHandler = SafeExamBrowser.Browser.Wrapper.Events.KeyEventHandler; + +namespace SafeExamBrowser.Browser.Wrapper +{ + internal interface ICefSharpControl : IChromiumWebBrowserBase, IWinFormsChromiumWebBrowser, IWin32Window, IComponent, ISynchronizeInvoke + { + event AuthCredentialsEventHandler AuthCredentialsRequired; + event BeforeBrowseEventHandler BeforeBrowse; + event BeforeDownloadEventHandler BeforeDownload; + event DownloadUpdatedEventHandler DownloadUpdated; + event FaviconUrlChangedEventHandler FaviconUrlChanged; + event FileDialogRequestedEventHandler FileDialogRequested; + event KeyEventHandler KeyEvent; + event LoadingProgressChangedEventHandler LoadingProgressChanged; + event OpenUrlFromTabEventHandler OpenUrlFromTab; + event PreKeyEventHandler PreKeyEvent; + event ResourceRequestEventHandler ResourceRequestHandlerRequired; + + void Dispose(bool disposing); + void GetAuthCredentials(IWebBrowser webBrowser, IBrowser browser, string originUrl, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback, GenericEventArgs args); + void GetResourceRequestHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling, ResourceRequestEventArgs args); + void Load(string address); + void OnBeforeBrowse(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, bool userGesture, bool isRedirect, GenericEventArgs args); + void OnBeforeDownload(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback); + void OnDownloadUpdated(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback); + void OnFaviconUrlChange(IWebBrowser webBrowser, IBrowser browser, IList urls); + void OnFileDialog(IWebBrowser webBrowser, IBrowser browser, CefFileDialogMode mode, CefFileDialogFlags flags, string title, string defaultFilePath, List acceptFilters, int selectedAcceptFilter, IFileDialogCallback callback); + void OnKeyEvent(IWebBrowser webBrowser, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey); + void OnLoadingProgressChange(IWebBrowser webBrowser, IBrowser browser, double progress); + void OnOpenUrlFromTab(IWebBrowser webBrowser, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture, GenericEventArgs args); + void OnPreKeyEvent(IWebBrowser webBrowser, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut, GenericEventArgs args); + } +} diff --git a/SafeExamBrowser.Browser/packages.config b/SafeExamBrowser.Browser/packages.config index 2b5a1924..cfe2acf1 100644 --- a/SafeExamBrowser.Browser/packages.config +++ b/SafeExamBrowser.Browser/packages.config @@ -1,9 +1,9 @@  - - - - + + + + diff --git a/SafeExamBrowser.Client/Notifications/AboutNotification.cs b/SafeExamBrowser.Client/Notifications/AboutNotification.cs index fe6216d8..71889a19 100644 --- a/SafeExamBrowser.Client/Notifications/AboutNotification.cs +++ b/SafeExamBrowser.Client/Notifications/AboutNotification.cs @@ -20,7 +20,6 @@ namespace SafeExamBrowser.Client.Notifications internal class AboutNotification : INotification { private readonly AppConfig appConfig; - private readonly IText text; private readonly IUserInterfaceFactory uiFactory; private IWindow window; @@ -33,7 +32,6 @@ namespace SafeExamBrowser.Client.Notifications public AboutNotification(AppConfig appConfig, IText text, IUserInterfaceFactory uiFactory) { this.appConfig = appConfig; - this.text = text; this.uiFactory = uiFactory; IconResource = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/AboutNotification.xaml") }; @@ -42,11 +40,11 @@ namespace SafeExamBrowser.Client.Notifications public void Activate() { - if (window == default(IWindow)) + if (window == default) { window = uiFactory.CreateAboutWindow(appConfig); - window.Closing += () => window = default(IWindow); + window.Closed += () => window = default; window.Show(); } else diff --git a/SafeExamBrowser.Client/Notifications/LogNotification.cs b/SafeExamBrowser.Client/Notifications/LogNotification.cs index 34b09eaa..6469bb6a 100644 --- a/SafeExamBrowser.Client/Notifications/LogNotification.cs +++ b/SafeExamBrowser.Client/Notifications/LogNotification.cs @@ -20,7 +20,6 @@ namespace SafeExamBrowser.Client.Notifications internal class LogNotification : INotification { private readonly ILogger logger; - private readonly IText text; private readonly IUserInterfaceFactory uiFactory; private IWindow window; @@ -33,7 +32,6 @@ namespace SafeExamBrowser.Client.Notifications public LogNotification(ILogger logger, IText text, IUserInterfaceFactory uiFactory) { this.logger = logger; - this.text = text; this.uiFactory = uiFactory; IconResource = new BitmapIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/LogNotification.ico") }; @@ -42,11 +40,11 @@ namespace SafeExamBrowser.Client.Notifications public void Activate() { - if (window == default(IWindow)) + if (window == default) { window = uiFactory.CreateLogWindow(logger); - window.Closing += () => window = null; + window.Closed += () => window = default; window.Show(); } else diff --git a/SafeExamBrowser.Proctoring/SafeExamBrowser.Proctoring.csproj b/SafeExamBrowser.Proctoring/SafeExamBrowser.Proctoring.csproj index 4ab419f9..1d3186e0 100644 --- a/SafeExamBrowser.Proctoring/SafeExamBrowser.Proctoring.csproj +++ b/SafeExamBrowser.Proctoring/SafeExamBrowser.Proctoring.csproj @@ -52,14 +52,14 @@ MinimumRecommendedRules.ruleset - - ..\packages\Microsoft.Web.WebView2.1.0.1072.54\lib\net45\Microsoft.Web.WebView2.Core.dll + + ..\packages\Microsoft.Web.WebView2.1.0.1108.44\lib\net45\Microsoft.Web.WebView2.Core.dll - - ..\packages\Microsoft.Web.WebView2.1.0.1072.54\lib\net45\Microsoft.Web.WebView2.WinForms.dll + + ..\packages\Microsoft.Web.WebView2.1.0.1108.44\lib\net45\Microsoft.Web.WebView2.WinForms.dll - - ..\packages\Microsoft.Web.WebView2.1.0.1072.54\lib\net45\Microsoft.Web.WebView2.Wpf.dll + + ..\packages\Microsoft.Web.WebView2.1.0.1108.44\lib\net45\Microsoft.Web.WebView2.Wpf.dll ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll @@ -124,11 +124,11 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + \ No newline at end of file diff --git a/SafeExamBrowser.Proctoring/packages.config b/SafeExamBrowser.Proctoring/packages.config index a4e759f2..3ee1f7e3 100644 --- a/SafeExamBrowser.Proctoring/packages.config +++ b/SafeExamBrowser.Proctoring/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/SafeExamBrowser.Runtime/CompositionRoot.cs b/SafeExamBrowser.Runtime/CompositionRoot.cs index 5baaeba4..51aae8ae 100644 --- a/SafeExamBrowser.Runtime/CompositionRoot.cs +++ b/SafeExamBrowser.Runtime/CompositionRoot.cs @@ -120,13 +120,13 @@ namespace SafeExamBrowser.Runtime } internal void LogStartupInformation() - { + { logger.Log($"/* {appConfig.ProgramTitle}, Version {appConfig.ProgramInformationalVersion}, Build {appConfig.ProgramBuildVersion}"); logger.Log($"/* {appConfig.ProgramCopyright}"); logger.Log($"/* "); logger.Log($"/* Please visit https://www.github.com/SafeExamBrowser for more information."); logger.Log(string.Empty); - logger.Log($"# Application started at {appConfig.ApplicationStartTime.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); + logger.Log($"# Application started at {appConfig.ApplicationStartTime:yyyy-MM-dd HH:mm:ss.fff}"); logger.Log($"# Running on {systemInfo.OperatingSystemInfo}"); logger.Log($"# Computer '{systemInfo.Name}' is a {systemInfo.Model} manufactured by {systemInfo.Manufacturer}"); logger.Log($"# Runtime-ID: {appConfig.RuntimeId}"); @@ -135,7 +135,7 @@ namespace SafeExamBrowser.Runtime internal void LogShutdownInformation() { - logger?.Log($"# Application terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); + logger?.Log($"# Application terminated at {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}"); } private void InitializeConfiguration() diff --git a/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserControl.cs b/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserControl.cs index bb2f3f2a..3ec45476 100644 --- a/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserControl.cs +++ b/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserControl.cs @@ -31,6 +31,11 @@ namespace SafeExamBrowser.UserInterface.Contracts.Browser /// bool CanNavigateForwards { get; } + /// + /// The user interface control to be embedded in an . + /// + object EmbeddableControl { get; } + /// /// Event fired when the address of the browser control changes. /// diff --git a/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj b/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj index 85db6dc3..21563d27 100644 --- a/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj +++ b/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj @@ -94,6 +94,7 @@ + diff --git a/SafeExamBrowser.UserInterface.Contracts/Windows/Events/WindowClosedEventHandler.cs b/SafeExamBrowser.UserInterface.Contracts/Windows/Events/WindowClosedEventHandler.cs new file mode 100644 index 00000000..d988c0fb --- /dev/null +++ b/SafeExamBrowser.UserInterface.Contracts/Windows/Events/WindowClosedEventHandler.cs @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * 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/. + */ + +namespace SafeExamBrowser.UserInterface.Contracts.Windows.Events +{ + /// + /// Indicates that a window has been closed. + /// + public delegate void WindowClosedEventHandler(); +} diff --git a/SafeExamBrowser.UserInterface.Contracts/Windows/IWindow.cs b/SafeExamBrowser.UserInterface.Contracts/Windows/IWindow.cs index 14679f30..381830ab 100644 --- a/SafeExamBrowser.UserInterface.Contracts/Windows/IWindow.cs +++ b/SafeExamBrowser.UserInterface.Contracts/Windows/IWindow.cs @@ -15,6 +15,11 @@ namespace SafeExamBrowser.UserInterface.Contracts.Windows /// public interface IWindow { + /// + /// Event fired when the window has been closed; + /// + event WindowClosedEventHandler Closed; + /// /// Event fired when the window is closing. /// diff --git a/SafeExamBrowser.UserInterface.Desktop/Windows/AboutWindow.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Windows/AboutWindow.xaml.cs index de5215d9..0b83fbc2 100644 --- a/SafeExamBrowser.UserInterface.Desktop/Windows/AboutWindow.xaml.cs +++ b/SafeExamBrowser.UserInterface.Desktop/Windows/AboutWindow.xaml.cs @@ -18,10 +18,18 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows { internal partial class AboutWindow : Window, IWindow { - private AppConfig appConfig; - private IText text; + private readonly AppConfig appConfig; + private readonly IText text; + + private WindowClosedEventHandler closed; private WindowClosingEventHandler closing; + event WindowClosedEventHandler IWindow.Closed + { + add { closed += value; } + remove { closed -= value; } + } + event WindowClosingEventHandler IWindow.Closing { add { closing += value; } @@ -44,6 +52,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows private void InitializeAboutWindow() { + Closed += (o, args) => closed?.Invoke(); Closing += (o, args) => closing?.Invoke(); MainText.Inlines.InsertBefore(MainText.Inlines.FirstInline, new Run(text.Get(TextKey.AboutWindow_LicenseInfo))); Title = text.Get(TextKey.AboutWindow_Title); diff --git a/SafeExamBrowser.UserInterface.Desktop/Windows/BrowserWindow.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Windows/BrowserWindow.xaml.cs index a74ea6c7..99f2c2fe 100644 --- a/SafeExamBrowser.UserInterface.Desktop/Windows/BrowserWindow.xaml.cs +++ b/SafeExamBrowser.UserInterface.Desktop/Windows/BrowserWindow.xaml.cs @@ -30,9 +30,11 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows { internal partial class BrowserWindow : Window, IBrowserWindow { - private bool isMainWindow; - private BrowserSettings settings; - private IText text; + private readonly bool isMainWindow; + private readonly BrowserSettings settings; + private readonly IText text; + + private WindowClosedEventHandler closed; private WindowClosingEventHandler closing; private WindowSettings WindowSettings @@ -55,6 +57,12 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows public event ActionRequestedEventHandler ZoomOutRequested; public event ActionRequestedEventHandler ZoomResetRequested; + event WindowClosedEventHandler IWindow.Closed + { + add { closed += value; } + remove { closed -= value; } + } + event WindowClosingEventHandler IWindow.Closing { add { closing += value; } @@ -282,7 +290,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows private void InitializeBrowserWindow(IBrowserControl browserControl) { - if (browserControl is System.Windows.Forms.Control control) + if (browserControl.EmbeddableControl is System.Windows.Forms.Control control) { BrowserControlHost.Child = control; } @@ -297,6 +305,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows private void RegisterEvents() { BackwardButton.Click += (o, args) => BackwardNavigationRequested?.Invoke(); + Closed += (o, args) => closed?.Invoke(); Closing += BrowserWindow_Closing; DeveloperConsoleButton.Click += (o, args) => DeveloperConsoleRequested?.Invoke(); DownloadsButton.Click += (o, args) => DownloadsPopup.IsOpen = !DownloadsPopup.IsOpen; diff --git a/SafeExamBrowser.UserInterface.Desktop/Windows/LockScreen.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Windows/LockScreen.xaml.cs index 1f2e3ec9..a34a18fb 100644 --- a/SafeExamBrowser.UserInterface.Desktop/Windows/LockScreen.xaml.cs +++ b/SafeExamBrowser.UserInterface.Desktop/Windows/LockScreen.xaml.cs @@ -25,9 +25,16 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows { internal partial class LockScreen : Window, ILockScreen { - private AutoResetEvent autoResetEvent; + private readonly AutoResetEvent autoResetEvent; + private readonly IText text; + private IList windows; - private IText text; + + event WindowClosedEventHandler IWindow.Closed + { + add { throw new NotImplementedException(); } + remove { throw new NotImplementedException(); } + } event WindowClosingEventHandler IWindow.Closing { diff --git a/SafeExamBrowser.UserInterface.Desktop/Windows/LogWindow.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Windows/LogWindow.xaml.cs index a4376b1b..3fca2522 100644 --- a/SafeExamBrowser.UserInterface.Desktop/Windows/LogWindow.xaml.cs +++ b/SafeExamBrowser.UserInterface.Desktop/Windows/LogWindow.xaml.cs @@ -19,10 +19,18 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows { internal partial class LogWindow : Window, IWindow { - private ILogger logger; - private LogViewModel model; + private readonly ILogger logger; + private readonly LogViewModel model; + + private WindowClosedEventHandler closed; private WindowClosingEventHandler closing; + event WindowClosedEventHandler IWindow.Closed + { + add { closed += value; } + remove { closed -= value; } + } + event WindowClosingEventHandler IWindow.Closing { add { closing += value; } @@ -70,6 +78,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows private void InitializeLogWindow() { DataContext = model; + Closed += (o, args) => closed?.Invoke(); Closing += LogWindow_Closing; Loaded += LogWindow_Loaded; } diff --git a/SafeExamBrowser.UserInterface.Desktop/Windows/PasswordDialog.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Windows/PasswordDialog.xaml.cs index 03b568da..70437978 100644 --- a/SafeExamBrowser.UserInterface.Desktop/Windows/PasswordDialog.xaml.cs +++ b/SafeExamBrowser.UserInterface.Desktop/Windows/PasswordDialog.xaml.cs @@ -17,9 +17,17 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows { internal partial class PasswordDialog : Window, IPasswordDialog { - private IText text; + private readonly IText text; + + private WindowClosedEventHandler closed; private WindowClosingEventHandler closing; + event WindowClosedEventHandler IWindow.Closed + { + add { closed += value; } + remove { closed -= value; } + } + event WindowClosingEventHandler IWindow.Closing { add { closing += value; } @@ -73,6 +81,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows ConfirmButton.Content = text.Get(TextKey.PasswordDialog_Confirm); ConfirmButton.Click += ConfirmButton_Click; + Closed += (o, args) => closed?.Invoke(); Closing += (o, args) => closing?.Invoke(); Loaded += (o, args) => Activate(); Password.KeyUp += Password_KeyUp; diff --git a/SafeExamBrowser.UserInterface.Desktop/Windows/ProctoringWindow.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Windows/ProctoringWindow.xaml.cs index fd7fcb64..2be0d215 100644 --- a/SafeExamBrowser.UserInterface.Desktop/Windows/ProctoringWindow.xaml.cs +++ b/SafeExamBrowser.UserInterface.Desktop/Windows/ProctoringWindow.xaml.cs @@ -18,8 +18,15 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows { public partial class ProctoringWindow : Window, IProctoringWindow { + private WindowClosedEventHandler closed; private WindowClosingEventHandler closing; + event WindowClosedEventHandler IWindow.Closed + { + add { closed += value; } + remove { closed -= value; } + } + event WindowClosingEventHandler IWindow.Closing { add { closing += value; } @@ -99,6 +106,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows control.FullScreenChanged += Control_FullScreenChanged; } + Closed += (o, args) => closed?.Invoke(); Closing += ProctoringWindow_Closing; Loaded += ProctoringWindow_Loaded; Top = SystemParameters.WorkArea.Height - Height - 15; diff --git a/SafeExamBrowser.UserInterface.Desktop/Windows/RuntimeWindow.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Windows/RuntimeWindow.xaml.cs index 4b5b4dc2..b1391024 100644 --- a/SafeExamBrowser.UserInterface.Desktop/Windows/RuntimeWindow.xaml.cs +++ b/SafeExamBrowser.UserInterface.Desktop/Windows/RuntimeWindow.xaml.cs @@ -20,10 +20,13 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows { internal partial class RuntimeWindow : Window, IRuntimeWindow { + private readonly AppConfig appConfig; + private readonly IText text; + private bool allowClose; - private AppConfig appConfig; - private IText text; private RuntimeWindowViewModel model; + + private WindowClosedEventHandler closed; private WindowClosingEventHandler closing; public bool ShowLog @@ -48,6 +51,12 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows set => Dispatcher.Invoke(() => Topmost = value); } + event WindowClosedEventHandler IWindow.Closed + { + add { closed += value; } + remove { closed -= value; } + } + event WindowClosingEventHandler IWindow.Closing { add { closing += value; } @@ -74,6 +83,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows { allowClose = true; model.BusyIndication = false; + closing?.Invoke(); base.Close(); }); @@ -145,6 +155,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows ProgressBar.DataContext = model; StatusTextBlock.DataContext = model; + Closed += (o, args) => closed?.Invoke(); Closing += (o, args) => args.Cancel = !allowClose; #if DEBUG diff --git a/SafeExamBrowser.UserInterface.Desktop/Windows/SplashScreen.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Windows/SplashScreen.xaml.cs index b80f8717..5a40245d 100644 --- a/SafeExamBrowser.UserInterface.Desktop/Windows/SplashScreen.xaml.cs +++ b/SafeExamBrowser.UserInterface.Desktop/Windows/SplashScreen.xaml.cs @@ -19,10 +19,13 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows { internal partial class SplashScreen : Window, ISplashScreen { - private bool allowClose; - private ProgressIndicatorViewModel model = new ProgressIndicatorViewModel(); + private readonly ProgressIndicatorViewModel model; + private readonly IText text; + private AppConfig appConfig; - private IText text; + private bool allowClose; + + private WindowClosedEventHandler closed; private WindowClosingEventHandler closing; public AppConfig AppConfig @@ -37,6 +40,12 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows } } + event WindowClosedEventHandler IWindow.Closed + { + add { closed += value; } + remove { closed -= value; } + } + event WindowClosingEventHandler IWindow.Closing { add { closing += value; } @@ -46,6 +55,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows internal SplashScreen(IText text, AppConfig appConfig = null) { this.appConfig = appConfig; + this.model = new ProgressIndicatorViewModel(); this.text = text; InitializeComponent(); @@ -63,6 +73,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows { allowClose = true; model.BusyIndication = false; + closing?.Invoke(); base.Close(); }); @@ -116,6 +127,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows StatusTextBlock.DataContext = model; ProgressBar.DataContext = model; + Closed += (o, args) => closed?.Invoke(); Closing += (o, args) => args.Cancel = !allowClose; } diff --git a/SafeExamBrowser.UserInterface.Mobile/Windows/AboutWindow.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Windows/AboutWindow.xaml.cs index fc5bde68..0be44bfc 100644 --- a/SafeExamBrowser.UserInterface.Mobile/Windows/AboutWindow.xaml.cs +++ b/SafeExamBrowser.UserInterface.Mobile/Windows/AboutWindow.xaml.cs @@ -18,10 +18,18 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows { internal partial class AboutWindow : Window, IWindow { - private AppConfig appConfig; - private IText text; + private readonly AppConfig appConfig; + private readonly IText text; + + private WindowClosedEventHandler closed; private WindowClosingEventHandler closing; + event WindowClosedEventHandler IWindow.Closed + { + add { closed += value; } + remove { closed -= value; } + } + event WindowClosingEventHandler IWindow.Closing { add { closing += value; } @@ -44,6 +52,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows private void InitializeAboutWindow() { + Closed += (o, args) => closed?.Invoke(); Closing += (o, args) => closing?.Invoke(); MainText.Inlines.InsertBefore(MainText.Inlines.FirstInline, new Run(text.Get(TextKey.AboutWindow_LicenseInfo))); Title = text.Get(TextKey.AboutWindow_Title); diff --git a/SafeExamBrowser.UserInterface.Mobile/Windows/BrowserWindow.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Windows/BrowserWindow.xaml.cs index 2c72eeeb..f2ffe1b4 100644 --- a/SafeExamBrowser.UserInterface.Mobile/Windows/BrowserWindow.xaml.cs +++ b/SafeExamBrowser.UserInterface.Mobile/Windows/BrowserWindow.xaml.cs @@ -30,9 +30,11 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows { internal partial class BrowserWindow : Window, IBrowserWindow { - private bool isMainWindow; - private BrowserSettings settings; - private IText text; + private readonly bool isMainWindow; + private readonly BrowserSettings settings; + private readonly IText text; + + private WindowClosedEventHandler closed; private WindowClosingEventHandler closing; private WindowSettings WindowSettings @@ -55,6 +57,12 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows public event ActionRequestedEventHandler ZoomOutRequested; public event ActionRequestedEventHandler ZoomResetRequested; + event WindowClosedEventHandler IWindow.Closed + { + add { closed += value; } + remove { closed -= value; } + } + event WindowClosingEventHandler IWindow.Closing { add { closing += value; } @@ -282,7 +290,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows private void InitializeBrowserWindow(IBrowserControl browserControl) { - if (browserControl is System.Windows.Forms.Control control) + if (browserControl.EmbeddableControl is System.Windows.Forms.Control control) { BrowserControlHost.Child = control; } @@ -297,6 +305,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows private void RegisterEvents() { BackwardButton.Click += (o, args) => BackwardNavigationRequested?.Invoke(); + Closed += (o, args) => closed?.Invoke(); Closing += BrowserWindow_Closing; DeveloperConsoleButton.Click += (o, args) => DeveloperConsoleRequested?.Invoke(); DownloadsButton.Click += (o, args) => DownloadsPopup.IsOpen = !DownloadsPopup.IsOpen; diff --git a/SafeExamBrowser.UserInterface.Mobile/Windows/LockScreen.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Windows/LockScreen.xaml.cs index 862cd9e7..dc4a5a39 100644 --- a/SafeExamBrowser.UserInterface.Mobile/Windows/LockScreen.xaml.cs +++ b/SafeExamBrowser.UserInterface.Mobile/Windows/LockScreen.xaml.cs @@ -25,9 +25,16 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows { internal partial class LockScreen : Window, ILockScreen { - private AutoResetEvent autoResetEvent; + private readonly AutoResetEvent autoResetEvent; + private readonly IText text; + private IList windows; - private IText text; + + event WindowClosedEventHandler IWindow.Closed + { + add { throw new NotImplementedException(); } + remove { throw new NotImplementedException(); } + } event WindowClosingEventHandler IWindow.Closing { diff --git a/SafeExamBrowser.UserInterface.Mobile/Windows/LogWindow.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Windows/LogWindow.xaml.cs index a44c4fcb..3b29f951 100644 --- a/SafeExamBrowser.UserInterface.Mobile/Windows/LogWindow.xaml.cs +++ b/SafeExamBrowser.UserInterface.Mobile/Windows/LogWindow.xaml.cs @@ -19,10 +19,18 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows { internal partial class LogWindow : Window, IWindow { - private ILogger logger; - private LogViewModel model; + private readonly ILogger logger; + private readonly LogViewModel model; + + private WindowClosedEventHandler closed; private WindowClosingEventHandler closing; + event WindowClosedEventHandler IWindow.Closed + { + add { closed += value; } + remove { closed -= value; } + } + event WindowClosingEventHandler IWindow.Closing { add { closing += value; } @@ -70,6 +78,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows private void InitializeLogWindow() { DataContext = model; + Closed += (o, args) => closed?.Invoke(); Closing += LogWindow_Closing; Loaded += LogWindow_Loaded; } diff --git a/SafeExamBrowser.UserInterface.Mobile/Windows/PasswordDialog.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Windows/PasswordDialog.xaml.cs index 3a3237e9..b75bed94 100644 --- a/SafeExamBrowser.UserInterface.Mobile/Windows/PasswordDialog.xaml.cs +++ b/SafeExamBrowser.UserInterface.Mobile/Windows/PasswordDialog.xaml.cs @@ -17,9 +17,17 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows { internal partial class PasswordDialog : Window, IPasswordDialog { - private IText text; + private readonly IText text; + + private WindowClosedEventHandler closed; private WindowClosingEventHandler closing; + event WindowClosedEventHandler IWindow.Closed + { + add { closed += value; } + remove { closed -= value; } + } + event WindowClosingEventHandler IWindow.Closing { add { closing += value; } @@ -83,6 +91,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows ConfirmButton.Content = text.Get(TextKey.PasswordDialog_Confirm); ConfirmButton.Click += ConfirmButton_Click; + Closed += (o, args) => closed?.Invoke(); Closing += (o, args) => closing?.Invoke(); Loaded += (o, args) => Activate(); Password.KeyUp += Password_KeyUp; diff --git a/SafeExamBrowser.UserInterface.Mobile/Windows/ProctoringWindow.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Windows/ProctoringWindow.xaml.cs index 34565bb4..0d153cd1 100644 --- a/SafeExamBrowser.UserInterface.Mobile/Windows/ProctoringWindow.xaml.cs +++ b/SafeExamBrowser.UserInterface.Mobile/Windows/ProctoringWindow.xaml.cs @@ -18,8 +18,15 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows { public partial class ProctoringWindow : Window, IProctoringWindow { + private WindowClosedEventHandler closed; private WindowClosingEventHandler closing; + event WindowClosedEventHandler IWindow.Closed + { + add { closed += value; } + remove { closed -= value; } + } + event WindowClosingEventHandler IWindow.Closing { add { closing += value; } @@ -99,6 +106,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows control.FullScreenChanged += Control_FullScreenChanged; } + Closed += (o, args) => closed?.Invoke(); Closing += ProctoringWindow_Closing; Loaded += ProctoringWindow_Loaded; Top = SystemParameters.WorkArea.Height - Height - 15; diff --git a/SafeExamBrowser.UserInterface.Mobile/Windows/RuntimeWindow.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Windows/RuntimeWindow.xaml.cs index a1ec0cea..b5060a70 100644 --- a/SafeExamBrowser.UserInterface.Mobile/Windows/RuntimeWindow.xaml.cs +++ b/SafeExamBrowser.UserInterface.Mobile/Windows/RuntimeWindow.xaml.cs @@ -20,10 +20,13 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows { internal partial class RuntimeWindow : Window, IRuntimeWindow { + private readonly AppConfig appConfig; + private readonly IText text; + private bool allowClose; - private AppConfig appConfig; - private IText text; private RuntimeWindowViewModel model; + + private WindowClosedEventHandler closed; private WindowClosingEventHandler closing; public bool ShowLog @@ -48,6 +51,12 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows set => Dispatcher.Invoke(() => Topmost = value); } + event WindowClosedEventHandler IWindow.Closed + { + add { closed += value; } + remove { closed -= value; } + } + event WindowClosingEventHandler IWindow.Closing { add { closing += value; } @@ -74,6 +83,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows { allowClose = true; model.BusyIndication = false; + closing?.Invoke(); base.Close(); }); @@ -145,6 +155,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows ProgressBar.DataContext = model; StatusTextBlock.DataContext = model; + Closed += (o, args) => closed?.Invoke(); Closing += (o, args) => args.Cancel = !allowClose; #if DEBUG diff --git a/SafeExamBrowser.UserInterface.Mobile/Windows/SplashScreen.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Windows/SplashScreen.xaml.cs index 117ae408..7b0c94b0 100644 --- a/SafeExamBrowser.UserInterface.Mobile/Windows/SplashScreen.xaml.cs +++ b/SafeExamBrowser.UserInterface.Mobile/Windows/SplashScreen.xaml.cs @@ -19,10 +19,13 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows { internal partial class SplashScreen : Window, ISplashScreen { + private readonly ProgressIndicatorViewModel model; + private readonly IText text; + private bool allowClose; - private ProgressIndicatorViewModel model = new ProgressIndicatorViewModel(); private AppConfig appConfig; - private IText text; + + private WindowClosedEventHandler closed; private WindowClosingEventHandler closing; public AppConfig AppConfig @@ -37,6 +40,12 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows } } + event WindowClosedEventHandler IWindow.Closed + { + add { closed += value; } + remove { closed -= value; } + } + event WindowClosingEventHandler IWindow.Closing { add { closing += value; } @@ -46,6 +55,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows internal SplashScreen(IText text, AppConfig appConfig = null) { this.appConfig = appConfig; + this.model = new ProgressIndicatorViewModel(); this.text = text; InitializeComponent(); @@ -63,6 +73,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows { allowClose = true; model.BusyIndication = false; + closing?.Invoke(); base.Close(); }); @@ -116,6 +127,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows StatusTextBlock.DataContext = model; ProgressBar.DataContext = model; + Closed += (o, args) => closed?.Invoke(); Closing += (o, args) => args.Cancel = !allowClose; }