diff --git a/SafeExamBrowser.Browser.Contracts/IBrowserApplication.cs b/SafeExamBrowser.Browser.Contracts/IBrowserApplication.cs index e1d9df7a..5ca4175c 100644 --- a/SafeExamBrowser.Browser.Contracts/IBrowserApplication.cs +++ b/SafeExamBrowser.Browser.Contracts/IBrowserApplication.cs @@ -6,7 +6,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -using System; using SafeExamBrowser.Applications.Contracts; using SafeExamBrowser.Browser.Contracts.Events; @@ -38,8 +37,8 @@ namespace SafeExamBrowser.Browser.Contracts event LoseFocusRequestedEventHandler LoseFocusRequested; /// - /// Transfers the focus to the browser window. - /// If true, the first focusable element in the browser window receives focus (passing forward of focus). Otherwise, the last element. + /// Transfers the focus to the browser application. If the parameter is true, the first focusable element in the browser window + /// receives focus (passing forward of focus). Otherwise, the last element receives focus. /// void Focus(bool forward); } diff --git a/SafeExamBrowser.Browser/BrowserApplication.cs b/SafeExamBrowser.Browser/BrowserApplication.cs index 9f2504dc..b3eedefc 100644 --- a/SafeExamBrowser.Browser/BrowserApplication.cs +++ b/SafeExamBrowser.Browser/BrowserApplication.cs @@ -87,6 +87,14 @@ namespace SafeExamBrowser.Browser this.windows = new List(); } + public void Focus(bool forward) + { + windows.ForEach(window => + { + window.Focus(forward); + }); + } + public IEnumerable GetWindows() { return new List(windows); @@ -459,13 +467,5 @@ namespace SafeExamBrowser.Browser CreateNewWindow(); logger.Info("Successfully reset browser."); } - - public void Focus(bool forward) - { - windows.ForEach(window => - { - window.Focus(forward); - }); - } } } diff --git a/SafeExamBrowser.Browser/BrowserControl.cs b/SafeExamBrowser.Browser/BrowserControl.cs index 2c9d0879..30ad6721 100644 --- a/SafeExamBrowser.Browser/BrowserControl.cs +++ b/SafeExamBrowser.Browser/BrowserControl.cs @@ -72,6 +72,23 @@ namespace SafeExamBrowser.Browser } } + public async void ExecuteJavascript(string javascript, Action callback) + { + var result = await control.EvaluateScriptAsync(javascript); + + callback(new JavascriptResult() + { + Message = result.Message, + Result = result.Result, + Success = result.Success + }); + } + + public void Find(string term, bool isInitial, bool caseSensitive, bool forward = true) + { + control.Find(term, forward, caseSensitive, !isInitial); + } + public void Initialize() { control.AddressChanged += (o, e) => AddressChanged?.Invoke(e.Address); @@ -85,7 +102,7 @@ namespace SafeExamBrowser.Browser 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.LoadError += (o, e) => LoadFailed?.Invoke((int) e.ErrorCode, e.ErrorText, e.Frame.IsMain, 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); @@ -94,28 +111,6 @@ namespace SafeExamBrowser.Browser control.TitleChanged += (o, e) => TitleChanged?.Invoke(e.Title); } - private void Control_FrameLoadStart(object sender, FrameLoadStartEventArgs e) - { - var browserExamKey = generator.CalculateBrowserExamKeyHash(e.Url); - var configurationKey = generator.CalculateConfigurationKeyHash(e.Url); - var api = contentLoader.LoadApi(browserExamKey, configurationKey, appConfig.ProgramBuildVersion); - - 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) - { - control.Find(term, forward, caseSensitive, !isInitial); - } - public void NavigateBackwards() { control.BrowserCore.GoBack(); @@ -146,18 +141,21 @@ namespace SafeExamBrowser.Browser control.BrowserCore.SetZoomLevel(level); } - /// - /// Executes the given Javascript code in the browser. - /// - public async void ExecuteJavascript(string javascript, Action callback) + private void Control_FrameLoadStart(object sender, FrameLoadStartEventArgs e) { - var result = await this.control.EvaluateScriptAsync(javascript); - callback(new JavascriptResult() + var browserExamKey = generator.CalculateBrowserExamKeyHash(e.Url); + var configurationKey = generator.CalculateConfigurationKeyHash(e.Url); + var api = contentLoader.LoadApi(browserExamKey, configurationKey, appConfig.ProgramBuildVersion); + + e.Frame.ExecuteJavaScriptAsync(api); + } + + private void Control_IsBrowserInitializedChanged(object sender, EventArgs e) + { + if (control.IsBrowserInitialized) { - Message = result.Message, - Result = result.Result, - Success = result.Success - }); + control.BrowserCore.GetHost().SetFocus(true); + } } } } diff --git a/SafeExamBrowser.Browser/BrowserWindow.cs b/SafeExamBrowser.Browser/BrowserWindow.cs index 0cf5c869..33a5ee16 100644 --- a/SafeExamBrowser.Browser/BrowserWindow.cs +++ b/SafeExamBrowser.Browser/BrowserWindow.cs @@ -127,6 +127,19 @@ namespace SafeExamBrowser.Browser Control.Destroy(); } + internal void Focus(bool forward) + { + if (forward) + { + window.FocusToolbar(forward); + } + else + { + window.FocusBrowser(); + Activate(); + } + } + internal void InitializeControl() { var cefSharpControl = default(ICefSharpControl); @@ -159,13 +172,13 @@ namespace SafeExamBrowser.Browser downloadHandler.DownloadAborted += DownloadHandler_DownloadAborted; downloadHandler.DownloadUpdated += DownloadHandler_DownloadUpdated; keyboardHandler.FindRequested += KeyboardHandler_FindRequested; + keyboardHandler.FocusAddressBarRequested += KeyboardHandler_FocusAddressBarRequested; keyboardHandler.HomeNavigationRequested += HomeNavigationRequested; keyboardHandler.ReloadRequested += ReloadRequested; + keyboardHandler.TabPressed += KeyboardHandler_TabPressed; keyboardHandler.ZoomInRequested += ZoomInRequested; keyboardHandler.ZoomOutRequested += ZoomOutRequested; keyboardHandler.ZoomResetRequested += ZoomResetRequested; - keyboardHandler.TabPressed += TabPressed; - keyboardHandler.FocusAddressBarRequested += FocusAddressBarRequested; resourceHandler.SessionIdentifierDetected += (id) => SessionIdentifierDetected?.Invoke(id); requestHandler.QuitUrlVisited += RequestHandler_QuitUrlVisited; requestHandler.RequestBlocked += RequestHandler_RequestBlocked; @@ -267,26 +280,38 @@ namespace SafeExamBrowser.Browser } } - private void Control_LoadFailed(int errorCode, string errorText, string url) + private void Control_LoadFailed(int errorCode, string errorText, bool isMainRequest, string url) { - if (errorCode == (int) CefErrorCode.None) + switch (errorCode) { - logger.Info($"Request{(WindowSettings.UrlPolicy.CanLog() ? $" for '{url}'" : "")} was successful."); + case (int) CefErrorCode.Aborted: + logger.Info($"Request{(WindowSettings.UrlPolicy.CanLog() ? $" for '{url}'" : "")} was aborted."); + break; + case (int) CefErrorCode.InternetDisconnected: + logger.Info($"Request{(WindowSettings.UrlPolicy.CanLog() ? $" for '{url}'" : "")} has failed due to loss of internet connection."); + break; + case (int) CefErrorCode.None: + logger.Info($"Request{(WindowSettings.UrlPolicy.CanLog() ? $" for '{url}'" : "")} was successful."); + break; + case (int) CefErrorCode.UnknownUrlScheme: + logger.Info($"Request{(WindowSettings.UrlPolicy.CanLog() ? $" for '{url}'" : "")} has an unknown URL scheme and will be handled by the OS."); + break; + default: + HandleUnknownLoadFailure(errorCode, errorText, isMainRequest, url); + break; } - else if (errorCode == (int) CefErrorCode.Aborted) - { - logger.Info($"Request{(WindowSettings.UrlPolicy.CanLog() ? $" for '{url}'" : "")} was aborted."); - } - else if (errorCode == (int) CefErrorCode.UnknownUrlScheme) - { - logger.Info($"Request{(WindowSettings.UrlPolicy.CanLog() ? $" for '{url}'" : "")} contains unknown URL scheme and will be handled by the OS."); - } - else + } + + private void HandleUnknownLoadFailure(int errorCode, string errorText, bool isMainRequest, string url) + { + var requestInfo = $"{errorText} ({errorCode}, {(isMainRequest ? "main" : "resource")} request)"; + + logger.Warn($"Request{(WindowSettings.UrlPolicy.CanLogError() ? $" for '{url}'" : "")} failed: {requestInfo}."); + + if (isMainRequest) { var title = text.Get(TextKey.Browser_LoadErrorTitle); - var message = text.Get(TextKey.Browser_LoadErrorMessage).Replace("%%URL%%", WindowSettings.UrlPolicy.CanLogError() ? url : "") + $" {errorText} ({errorCode})"; - - logger.Warn($"Request{(WindowSettings.UrlPolicy.CanLogError() ? $" for '{url}'" : "")} failed: {errorText} ({errorCode})."); + var message = text.Get(TextKey.Browser_LoadErrorMessage).Replace("%%URL%%", WindowSettings.UrlPolicy.CanLogError() ? url : "") + $" {requestInfo}"; Task.Run(() => messageBox.Show(message, title, icon: MessageBoxIcon.Error, parent: window)).ContinueWith(_ => Control.NavigateBackwards()); } @@ -465,11 +490,32 @@ namespace SafeExamBrowser.Browser } } - private void FocusAddressBarRequested() + private void KeyboardHandler_FocusAddressBarRequested() { window.FocusAddressBar(); } + private void KeyboardHandler_TabPressed(bool shiftPressed) + { + Control.ExecuteJavascript("document.activeElement.tagName", result => + { + var tagName = result.Result as string; + + if (tagName != null && tagName.ToUpper() == "BODY") + { + // This means the user is now at the start of the focus / tabIndex chain in the website. + if (shiftPressed) + { + window.FocusToolbar(!shiftPressed); + } + else + { + LoseFocusRequested?.Invoke(true); + } + } + }); + } + private ChromiumHostControl LifeSpanHandler_CreatePopup() { var args = new PopupRequestedEventArgs(); @@ -717,42 +763,6 @@ namespace SafeExamBrowser.Browser } } - private void TabPressed(bool shiftPressed) - { - Control.ExecuteJavascript("document.activeElement.tagName", result => - { - var tagName = result.Result as string; - if (tagName != null) - { - if (tagName.ToUpper() == "BODY") - { - // this means the user is now at the start of the focus / tabIndex chain in the website - if (shiftPressed) - { - window.FocusToolbar(!shiftPressed); - } - else - { - LoseFocusRequested?.Invoke(true); - } - } - } - }); - } - - internal void Focus(bool forward) - { - if (forward) - { - window.FocusToolbar(forward); - } - else - { - window.FocusBrowser(); - Activate(); - } - } - private double CalculateZoomPercentage() { return (zoomLevel * 25.0) + 100.0; diff --git a/SafeExamBrowser.UserInterface.Contracts/Browser/Events/LoadFailedEventHandler.cs b/SafeExamBrowser.UserInterface.Contracts/Browser/Events/LoadFailedEventHandler.cs index 6e490de4..c99e2c4a 100644 --- a/SafeExamBrowser.UserInterface.Contracts/Browser/Events/LoadFailedEventHandler.cs +++ b/SafeExamBrowser.UserInterface.Contracts/Browser/Events/LoadFailedEventHandler.cs @@ -11,5 +11,5 @@ namespace SafeExamBrowser.UserInterface.Contracts.Browser.Events /// /// Indicates a load error for a browser request. /// - public delegate void LoadFailedEventHandler(int errorCode, string errorText, string url); + public delegate void LoadFailedEventHandler(int errorCode, string errorText, bool isMainRequest, string url); } diff --git a/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserControl.cs b/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserControl.cs index a27c5c8e..c70a9125 100644 --- a/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserControl.cs +++ b/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserControl.cs @@ -6,6 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +using System; using SafeExamBrowser.UserInterface.Contracts.Browser.Data; using SafeExamBrowser.UserInterface.Contracts.Browser.Events; @@ -63,9 +64,9 @@ namespace SafeExamBrowser.UserInterface.Contracts.Browser void Destroy(); /// - /// Executes the given Javascript code in the browser. + /// Executes the given JavaScript code in the browser. /// - void ExecuteJavascript(string javascript, System.Action callback); + void ExecuteJavascript(string code, Action callback); /// /// Attempts to find the given term on the current page according to the specified parameters. diff --git a/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserWindow.cs b/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserWindow.cs index b41cc25d..f126b0a4 100644 --- a/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserWindow.cs +++ b/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserWindow.cs @@ -29,7 +29,7 @@ namespace SafeExamBrowser.UserInterface.Contracts.Browser /// Enables the forward navigation button. /// bool CanNavigateForwards { set; } - + /// /// The native handle of the window. /// @@ -90,6 +90,22 @@ namespace SafeExamBrowser.UserInterface.Contracts.Browser /// event ActionRequestedEventHandler ZoomResetRequested; + /// + /// Sets the focus on the address bar of the window. + /// + void FocusAddressBar(); + + /// + /// Sets the focus on the browser content of the window. + /// + void FocusBrowser(); + + /// + /// Sets the focus on the toolbar of the window. If the parameter is set to true, the first focusable control on the toolbar gets focused. + /// If it is set to false, the last one. + /// + void FocusToolbar(bool forward); + /// /// Displays the find toolbar to search the content of a page. /// @@ -129,18 +145,5 @@ namespace SafeExamBrowser.UserInterface.Contracts.Browser /// Updates the display value of the current page zoom. Value is expected to be in percentage. /// void UpdateZoomLevel(double value); - - /// - /// Sets the focus to the toolbar. - /// - /// If true, the first focusable control on the toolbar gets focused. If false, the last one. - void FocusToolbar(bool forward); - - /// - /// Sets the focus to the browser. - /// - void FocusBrowser(); - - void FocusAddressBar(); } }