SEBWIN-554, SEBWIN-544: Changed and improved load error handling (resource request do not trigger error message anymore).

This commit is contained in:
Damian Büchel 2022-05-13 16:47:18 +02:00
parent bc1583c070
commit bfe4a32098
7 changed files with 126 additions and 115 deletions

View file

@ -6,7 +6,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using System;
using SafeExamBrowser.Applications.Contracts; using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Browser.Contracts.Events; using SafeExamBrowser.Browser.Contracts.Events;
@ -38,8 +37,8 @@ namespace SafeExamBrowser.Browser.Contracts
event LoseFocusRequestedEventHandler LoseFocusRequested; event LoseFocusRequestedEventHandler LoseFocusRequested;
/// <summary> /// <summary>
/// Transfers the focus to the browser window. /// Transfers the focus to the browser application. If the parameter is <c>true</c>, the first focusable element in the browser window
/// <paramref name="forward">If true, the first focusable element in the browser window receives focus (passing forward of focus). Otherwise, the last element.</paramref> /// receives focus (passing forward of focus). Otherwise, the last element receives focus.
/// </summary> /// </summary>
void Focus(bool forward); void Focus(bool forward);
} }

View file

@ -87,6 +87,14 @@ namespace SafeExamBrowser.Browser
this.windows = new List<BrowserWindow>(); this.windows = new List<BrowserWindow>();
} }
public void Focus(bool forward)
{
windows.ForEach(window =>
{
window.Focus(forward);
});
}
public IEnumerable<IApplicationWindow> GetWindows() public IEnumerable<IApplicationWindow> GetWindows()
{ {
return new List<IApplicationWindow>(windows); return new List<IApplicationWindow>(windows);
@ -459,13 +467,5 @@ namespace SafeExamBrowser.Browser
CreateNewWindow(); CreateNewWindow();
logger.Info("Successfully reset browser."); logger.Info("Successfully reset browser.");
} }
public void Focus(bool forward)
{
windows.ForEach(window =>
{
window.Focus(forward);
});
}
} }
} }

View file

@ -72,6 +72,23 @@ namespace SafeExamBrowser.Browser
} }
} }
public async void ExecuteJavascript(string javascript, Action<JavascriptResult> 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() public void Initialize()
{ {
control.AddressChanged += (o, e) => AddressChanged?.Invoke(e.Address); control.AddressChanged += (o, e) => AddressChanged?.Invoke(e.Address);
@ -85,7 +102,7 @@ namespace SafeExamBrowser.Browser
control.FrameLoadStart += Control_FrameLoadStart; control.FrameLoadStart += Control_FrameLoadStart;
control.IsBrowserInitializedChanged += Control_IsBrowserInitializedChanged; control.IsBrowserInitializedChanged += Control_IsBrowserInitializedChanged;
control.KeyEvent += (w, b, t, k, n, m, s) => keyboardHandler.OnKeyEvent(w, b, t, k, n, m, s); 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.LoadingProgressChanged += (w, b, p) => displayHandler.OnLoadingProgressChange(w, b, p);
control.LoadingStateChanged += (o, e) => LoadingStateChanged?.Invoke(e.IsLoading); 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.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); 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() public void NavigateBackwards()
{ {
control.BrowserCore.GoBack(); control.BrowserCore.GoBack();
@ -146,18 +141,21 @@ namespace SafeExamBrowser.Browser
control.BrowserCore.SetZoomLevel(level); control.BrowserCore.SetZoomLevel(level);
} }
/// <summary> private void Control_FrameLoadStart(object sender, FrameLoadStartEventArgs e)
/// Executes the given Javascript code in the browser.
/// </summary>
public async void ExecuteJavascript(string javascript, Action<JavascriptResult> callback)
{ {
var result = await this.control.EvaluateScriptAsync(javascript); var browserExamKey = generator.CalculateBrowserExamKeyHash(e.Url);
callback(new JavascriptResult() 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, control.BrowserCore.GetHost().SetFocus(true);
Result = result.Result, }
Success = result.Success
});
} }
} }
} }

View file

@ -127,6 +127,19 @@ namespace SafeExamBrowser.Browser
Control.Destroy(); Control.Destroy();
} }
internal void Focus(bool forward)
{
if (forward)
{
window.FocusToolbar(forward);
}
else
{
window.FocusBrowser();
Activate();
}
}
internal void InitializeControl() internal void InitializeControl()
{ {
var cefSharpControl = default(ICefSharpControl); var cefSharpControl = default(ICefSharpControl);
@ -159,13 +172,13 @@ namespace SafeExamBrowser.Browser
downloadHandler.DownloadAborted += DownloadHandler_DownloadAborted; downloadHandler.DownloadAborted += DownloadHandler_DownloadAborted;
downloadHandler.DownloadUpdated += DownloadHandler_DownloadUpdated; downloadHandler.DownloadUpdated += DownloadHandler_DownloadUpdated;
keyboardHandler.FindRequested += KeyboardHandler_FindRequested; keyboardHandler.FindRequested += KeyboardHandler_FindRequested;
keyboardHandler.FocusAddressBarRequested += KeyboardHandler_FocusAddressBarRequested;
keyboardHandler.HomeNavigationRequested += HomeNavigationRequested; keyboardHandler.HomeNavigationRequested += HomeNavigationRequested;
keyboardHandler.ReloadRequested += ReloadRequested; keyboardHandler.ReloadRequested += ReloadRequested;
keyboardHandler.TabPressed += KeyboardHandler_TabPressed;
keyboardHandler.ZoomInRequested += ZoomInRequested; keyboardHandler.ZoomInRequested += ZoomInRequested;
keyboardHandler.ZoomOutRequested += ZoomOutRequested; keyboardHandler.ZoomOutRequested += ZoomOutRequested;
keyboardHandler.ZoomResetRequested += ZoomResetRequested; keyboardHandler.ZoomResetRequested += ZoomResetRequested;
keyboardHandler.TabPressed += TabPressed;
keyboardHandler.FocusAddressBarRequested += FocusAddressBarRequested;
resourceHandler.SessionIdentifierDetected += (id) => SessionIdentifierDetected?.Invoke(id); resourceHandler.SessionIdentifierDetected += (id) => SessionIdentifierDetected?.Invoke(id);
requestHandler.QuitUrlVisited += RequestHandler_QuitUrlVisited; requestHandler.QuitUrlVisited += RequestHandler_QuitUrlVisited;
requestHandler.RequestBlocked += RequestHandler_RequestBlocked; 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."); private void HandleUnknownLoadFailure(int errorCode, string errorText, bool isMainRequest, string url)
} {
else if (errorCode == (int) CefErrorCode.UnknownUrlScheme) var requestInfo = $"{errorText} ({errorCode}, {(isMainRequest ? "main" : "resource")} request)";
{
logger.Info($"Request{(WindowSettings.UrlPolicy.CanLog() ? $" for '{url}'" : "")} contains unknown URL scheme and will be handled by the OS."); logger.Warn($"Request{(WindowSettings.UrlPolicy.CanLogError() ? $" for '{url}'" : "")} failed: {requestInfo}.");
}
else if (isMainRequest)
{ {
var title = text.Get(TextKey.Browser_LoadErrorTitle); var title = text.Get(TextKey.Browser_LoadErrorTitle);
var message = text.Get(TextKey.Browser_LoadErrorMessage).Replace("%%URL%%", WindowSettings.UrlPolicy.CanLogError() ? url : "") + $" {errorText} ({errorCode})"; var message = text.Get(TextKey.Browser_LoadErrorMessage).Replace("%%URL%%", WindowSettings.UrlPolicy.CanLogError() ? url : "") + $" {requestInfo}";
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());
} }
@ -465,11 +490,32 @@ namespace SafeExamBrowser.Browser
} }
} }
private void FocusAddressBarRequested() private void KeyboardHandler_FocusAddressBarRequested()
{ {
window.FocusAddressBar(); 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() private ChromiumHostControl LifeSpanHandler_CreatePopup()
{ {
var args = new PopupRequestedEventArgs(); 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() private double CalculateZoomPercentage()
{ {
return (zoomLevel * 25.0) + 100.0; return (zoomLevel * 25.0) + 100.0;

View file

@ -11,5 +11,5 @@ namespace SafeExamBrowser.UserInterface.Contracts.Browser.Events
/// <summary> /// <summary>
/// Indicates a load error for a browser request. /// Indicates a load error for a browser request.
/// </summary> /// </summary>
public delegate void LoadFailedEventHandler(int errorCode, string errorText, string url); public delegate void LoadFailedEventHandler(int errorCode, string errorText, bool isMainRequest, string url);
} }

View file

@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * 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.Data;
using SafeExamBrowser.UserInterface.Contracts.Browser.Events; using SafeExamBrowser.UserInterface.Contracts.Browser.Events;
@ -63,9 +64,9 @@ namespace SafeExamBrowser.UserInterface.Contracts.Browser
void Destroy(); void Destroy();
/// <summary> /// <summary>
/// Executes the given Javascript code in the browser. /// Executes the given JavaScript code in the browser.
/// </summary> /// </summary>
void ExecuteJavascript(string javascript, System.Action<JavascriptResult> callback); void ExecuteJavascript(string code, Action<JavascriptResult> callback);
/// <summary> /// <summary>
/// Attempts to find the given term on the current page according to the specified parameters. /// Attempts to find the given term on the current page according to the specified parameters.

View file

@ -29,7 +29,7 @@ namespace SafeExamBrowser.UserInterface.Contracts.Browser
/// Enables the forward navigation button. /// Enables the forward navigation button.
/// </summary> /// </summary>
bool CanNavigateForwards { set; } bool CanNavigateForwards { set; }
/// <summary> /// <summary>
/// The native handle of the window. /// The native handle of the window.
/// </summary> /// </summary>
@ -90,6 +90,22 @@ namespace SafeExamBrowser.UserInterface.Contracts.Browser
/// </summary> /// </summary>
event ActionRequestedEventHandler ZoomResetRequested; event ActionRequestedEventHandler ZoomResetRequested;
/// <summary>
/// Sets the focus on the address bar of the window.
/// </summary>
void FocusAddressBar();
/// <summary>
/// Sets the focus on the browser content of the window.
/// </summary>
void FocusBrowser();
/// <summary>
/// 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.
/// </summary>
void FocusToolbar(bool forward);
/// <summary> /// <summary>
/// Displays the find toolbar to search the content of a page. /// Displays the find toolbar to search the content of a page.
/// </summary> /// </summary>
@ -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. /// Updates the display value of the current page zoom. Value is expected to be in percentage.
/// </summary> /// </summary>
void UpdateZoomLevel(double value); void UpdateZoomLevel(double value);
/// <summary>
/// Sets the focus to the toolbar.
/// </summary>
/// <param name="forward">If true, the first focusable control on the toolbar gets focused. If false, the last one.</param>
void FocusToolbar(bool forward);
/// <summary>
/// Sets the focus to the browser.
/// </summary>
void FocusBrowser();
void FocusAddressBar();
} }
} }