Update Safe Exam Browser Patch to 3.10.0.826

This commit is contained in:
2025-09-16 16:32:31 +02:00
parent 4827ae1afc
commit dd82d45ed8
320 changed files with 8445 additions and 5295 deletions

View File

@@ -18,6 +18,7 @@ using SafeExamBrowser.Applications.Contracts.Events;
using SafeExamBrowser.Browser.Contracts;
using SafeExamBrowser.Browser.Contracts.Events;
using SafeExamBrowser.Browser.Events;
using SafeExamBrowser.Browser.Integrations;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Core.Contracts.Resources.Icons;
@@ -119,6 +120,7 @@ namespace SafeExamBrowser.Browser
InitializeCookies();
InitializeDownAndUploadDirectory();
InitializeIntegrityKeys();
InitializePreferences();
logger.Info("Initialized browser.");
}
@@ -167,6 +169,12 @@ namespace SafeExamBrowser.Browser
private void CreateNewWindow(PopupRequestedEventArgs args = default)
{
var id = ++windowIdCounter;
var integrations = new Integration[]
{
new GenericIntegration(logger.CloneFor($"{nameof(GenericIntegration)} #{id}")),
new EdxIntegration(logger.CloneFor($"{nameof(EdxIntegration)} #{id}")),
new MoodleIntegration(logger.CloneFor($"{nameof(MoodleIntegration)} #{id}"))
};
var isMainWindow = windows.Count == 0;
var startUrl = GenerateStartUrl();
var windowLogger = logger.CloneFor($"Browser Window #{id}");
@@ -176,6 +184,7 @@ namespace SafeExamBrowser.Browser
fileSystemDialog,
hashAlgorithm,
id,
integrations,
isMainWindow,
keyGenerator,
windowLogger,
@@ -197,7 +206,7 @@ namespace SafeExamBrowser.Browser
window.InitializeControl();
windows.Add(window);
if (args != default(PopupRequestedEventArgs))
if (args != default)
{
args.Window = window;
}
@@ -213,6 +222,7 @@ namespace SafeExamBrowser.Browser
private void DeleteCookies()
{
var callback = new TaskDeleteCookiesCallback();
var cookieManager = Cef.GetGlobalCookieManager();
callback.Task.ContinueWith(task =>
{
@@ -226,7 +236,7 @@ namespace SafeExamBrowser.Browser
}
});
if (Cef.GetGlobalCookieManager().DeleteCookies(callback: callback))
if (cookieManager != default && cookieManager.DeleteCookies(callback: callback))
{
logger.Debug("Successfully initiated cookie deletion.");
}
@@ -316,7 +326,6 @@ namespace SafeExamBrowser.Browser
cefSettings.AcceptLanguageList = CultureInfo.CurrentUICulture.Name;
cefSettings.CachePath = appConfig.BrowserCachePath;
cefSettings.CefCommandLineArgs.Add("touch-events", "enabled");
cefSettings.LogFile = appConfig.BrowserLogFilePath;
cefSettings.LogSeverity = error ? LogSeverity.Error : (warning ? LogSeverity.Warning : LogSeverity.Info);
cefSettings.PersistSessionCookies = !settings.DeleteCookiesOnStartup || !settings.DeleteCookiesOnShutdown;
@@ -339,6 +348,7 @@ namespace SafeExamBrowser.Browser
cefSettings.CefCommandLineArgs.Add("enable-media-stream");
cefSettings.CefCommandLineArgs.Add("enable-usermedia-screen-capturing");
cefSettings.CefCommandLineArgs.Add("touch-events", "enabled");
cefSettings.CefCommandLineArgs.Add("use-fake-ui-for-media-stream");
InitializeProxySettings(cefSettings);
@@ -417,6 +427,18 @@ namespace SafeExamBrowser.Browser
}
}
private void InitializePreferences()
{
Cef.UIThreadTaskFactory.StartNew(() =>
{
using (var requestContext = Cef.GetGlobalRequestContext())
{
requestContext.SetPreference("autofill.credit_card_enabled", false, out _);
requestContext.SetPreference("autofill.profile_enabled", false, out _);
}
});
}
private void InitializeProxySettings(CefSettings cefSettings)
{
if (settings.Proxy.Policy == ProxyPolicy.Custom)
@@ -506,6 +528,7 @@ namespace SafeExamBrowser.Browser
private void Window_ResetRequested()
{
logger.Info("Attempting to reset browser...");
AwaitReady();
foreach (var window in windows)

View File

@@ -23,6 +23,7 @@ namespace SafeExamBrowser.Browser
{
private readonly Clipboard clipboard;
private readonly ICefSharpControl control;
private readonly IContextMenuHandler contextMenuHandler;
private readonly IDialogHandler dialogHandler;
private readonly IDisplayHandler displayHandler;
private readonly IDownloadHandler downloadHandler;
@@ -47,6 +48,7 @@ namespace SafeExamBrowser.Browser
public BrowserControl(
Clipboard clipboard,
ICefSharpControl control,
IContextMenuHandler contextMenuHandler,
IDialogHandler dialogHandler,
IDisplayHandler displayHandler,
IDownloadHandler downloadHandler,
@@ -58,8 +60,9 @@ namespace SafeExamBrowser.Browser
IRenderProcessMessageHandler renderProcessMessageHandler,
IRequestHandler requestHandler)
{
this.control = control;
this.clipboard = clipboard;
this.control = control;
this.contextMenuHandler = contextMenuHandler;
this.dialogHandler = dialogHandler;
this.displayHandler = displayHandler;
this.downloadHandler = downloadHandler;
@@ -89,31 +92,20 @@ namespace SafeExamBrowser.Browser
{
control.BrowserCore.EvaluateScriptAsync(code).ContinueWith(t =>
{
callback?.Invoke(new JavaScriptResult
{
Message = t.Result.Message,
Result = t.Result.Result,
Success = t.Result.Success
});
callback?.Invoke(new JavaScriptResult { Message = t.Result.Message, Result = t.Result.Result, Success = t.Result.Success });
});
}
else
{
Task.Run(() => callback?.Invoke(new JavaScriptResult
{
Message = "JavaScript can't be executed in main frame!",
Success = false
}));
Task.Run(() => callback?.Invoke(new JavaScriptResult { Message = "Could not execute JavaScript in main frame!", Success = false }));
}
}
catch (Exception e)
{
logger.Error($"Failed to execute JavaScript '{(code.Length > 50 ? code.Take(50) : code)}'!", e);
Task.Run(() => callback?.Invoke(new JavaScriptResult
{
Message = $"Failed to execute JavaScript '{(code.Length > 50 ? code.Take(50) : code)}'! Reason: {e.Message}",
Success = false
}));
var message = "Failed to execute JavaScript in main frame!";
logger.Error(message, e);
Task.Run(() => callback?.Invoke(new JavaScriptResult { Message = $"{message} Reason: {e.Message}", Success = false }));
}
}
@@ -129,10 +121,13 @@ namespace SafeExamBrowser.Browser
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.BeforeContextMenu += (w, b, f, p, m) => contextMenuHandler.OnBeforeContextMenu(w, b, f, p, m);
control.BeforeDownload += (w, b, d, c, a) => a.Value = a.Value = downloadHandler.OnBeforeDownload(w, b, d, c);
control.BeforeUnloadDialog += (w, b, m, r, c, a) => a.Value = javaScriptDialogHandler.OnBeforeUnloadDialog(w, b, m, r, c);
control.CanDownload += (w, b, u, r, a) => a.Value = downloadHandler.CanDownload(w, b, u, r);
control.ContextCreated += (w, b, f) => renderProcessMessageHandler.OnContextCreated(w, b, f);
control.ContextMenuCommand += (w, b, f, p, c, e, a) => a.Value = contextMenuHandler.OnContextMenuCommand(w, b, f, p, c, e);
control.ContextMenuDismissed += (w, b, f) => contextMenuHandler.OnContextMenuDismissed(w, b, f);
control.ContextReleased += (w, b, f) => renderProcessMessageHandler.OnContextReleased(w, b, f);
control.DialogClosed += (w, b) => javaScriptDialogHandler.OnDialogClosed(w, b);
control.DownloadUpdated += (w, b, d, c) => downloadHandler.OnDownloadUpdated(w, b, d, c);
@@ -152,6 +147,7 @@ namespace SafeExamBrowser.Browser
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.ResetDialogState += (w, b) => javaScriptDialogHandler.OnResetDialogState(w, b);
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.RunContextMenu += (w, b, f, p, m, c, a) => a.Value = contextMenuHandler.RunContextMenu(w, b, f, p, m, c);
control.SetFocus += (w, b, s, a) => a.Value = focusHandler.OnSetFocus(w, b, s);
control.TakeFocus += (w, b, n) => focusHandler.OnTakeFocus(w, b, n);
control.TitleChanged += (o, e) => TitleChanged?.Invoke(e.Title);
@@ -193,9 +189,21 @@ namespace SafeExamBrowser.Browser
control.BrowserCore.SetZoomLevel(level);
}
private void Clipboard_Changed(long id)
private void Clipboard_Changed(string id)
{
ExecuteJavaScript($"SafeExamBrowser.clipboard.update({id}, '{clipboard.Content}');");
try
{
var script = $"SafeExamBrowser.clipboard.update('{id}', '{clipboard.Content}');";
foreach (var frame in control.BrowserCore?.GetAllFrames() ?? Enumerable.Empty<IFrame>())
{
frame.EvaluateScriptAsync(script);
}
}
catch (Exception e)
{
logger.Error($"Failed to update JavaScript clipboard!", e);
}
}
private void Control_IsBrowserInitializedChanged(object sender, EventArgs e)
@@ -208,7 +216,7 @@ namespace SafeExamBrowser.Browser
private void WebBrowser_JavascriptMessageReceived(object sender, JavascriptMessageReceivedEventArgs e)
{
clipboard.Process(e);
clipboard.Update(e);
}
}
}

View File

@@ -19,6 +19,7 @@ using SafeExamBrowser.Browser.Contracts.Filters;
using SafeExamBrowser.Browser.Events;
using SafeExamBrowser.Browser.Filters;
using SafeExamBrowser.Browser.Handlers;
using SafeExamBrowser.Browser.Integrations;
using SafeExamBrowser.Browser.Wrapper;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
@@ -52,6 +53,7 @@ namespace SafeExamBrowser.Browser
private readonly IFileSystemDialog fileSystemDialog;
private readonly IHashAlgorithm hashAlgorithm;
private readonly HttpClient httpClient;
private readonly IEnumerable<Integration> integrations;
private readonly IKeyGenerator keyGenerator;
private readonly IModuleLogger logger;
private readonly IMessageBox messageBox;
@@ -97,6 +99,7 @@ namespace SafeExamBrowser.Browser
IFileSystemDialog fileSystemDialog,
IHashAlgorithm hashAlgorithm,
int id,
IEnumerable<Integration> integrations,
bool isMainWindow,
IKeyGenerator keyGenerator,
IModuleLogger logger,
@@ -113,6 +116,7 @@ namespace SafeExamBrowser.Browser
this.hashAlgorithm = hashAlgorithm;
this.httpClient = new HttpClient();
this.Id = id;
this.integrations = integrations;
this.IsMainWindow = isMainWindow;
this.keyGenerator = keyGenerator;
this.logger = logger;
@@ -153,6 +157,7 @@ namespace SafeExamBrowser.Browser
{
var cefSharpControl = default(ICefSharpControl);
var controlLogger = logger.CloneFor($"{nameof(BrowserControl)} #{Id}");
var contextMenuHandler = new ContextMenuHandler();
var dialogHandler = new DialogHandler();
var displayHandler = new DisplayHandler();
var downloadLogger = logger.CloneFor($"{nameof(DownloadHandler)} #{Id}");
@@ -164,7 +169,7 @@ namespace SafeExamBrowser.Browser
var renderHandler = new RenderProcessMessageHandler(appConfig, clipboard, keyGenerator, settings, text);
var requestFilter = new RequestFilter();
var requestLogger = logger.CloneFor($"{nameof(RequestHandler)} #{Id}");
var resourceHandler = new ResourceHandler(appConfig, requestFilter, keyGenerator, logger, sessionMode, settings, WindowSettings, text);
var resourceHandler = new ResourceHandler(appConfig, requestFilter, integrations, keyGenerator, logger, sessionMode, settings, WindowSettings, text);
var requestHandler = new RequestHandler(appConfig, requestFilter, requestLogger, resourceHandler, settings, WindowSettings);
Icon = new BrowserIconResource();
@@ -202,6 +207,7 @@ namespace SafeExamBrowser.Browser
Control = new BrowserControl(
clipboard,
cefSharpControl,
contextMenuHandler,
dialogHandler,
displayHandler,
downloadHandler,
@@ -218,12 +224,13 @@ namespace SafeExamBrowser.Browser
Control.TitleChanged += Control_TitleChanged;
Control.Initialize();
logger.Debug("Initialized browser control.");
}
internal void InitializeWindow()
{
window = uiFactory.CreateBrowserWindow(Control, settings, IsMainWindow, this.logger);
window = uiFactory.CreateBrowserWindow(Control, settings, IsMainWindow, logger);
window.AddressChanged += Window_AddressChanged;
window.BackwardNavigationRequested += Window_BackwardNavigationRequested;
window.Closed += Window_Closed;
@@ -242,6 +249,7 @@ namespace SafeExamBrowser.Browser
window.BringToForeground();
Handle = window.Handle;
InitiateCookieTraversal();
logger.Debug("Initialized browser window.");
}
@@ -256,6 +264,22 @@ namespace SafeExamBrowser.Browser
.Build();
}
private void InitiateCookieTraversal()
{
var visitor = new CookieVisitor(integrations);
visitor.UserIdentifierDetected += (id) => UserIdentifierDetected?.Invoke(id);
if (Cef.GetGlobalCookieManager().VisitAllCookies(visitor))
{
logger.Debug("Successfully initiated cookie traversal.");
}
else
{
logger.Warn("Failed to initiate cookie traversal!");
}
}
private void InitializeRequestFilter(IRequestFilter requestFilter)
{
if (settings.Filter.ProcessContentRequests || settings.Filter.ProcessMainRequests)
@@ -297,8 +321,6 @@ namespace SafeExamBrowser.Browser
window.UpdateTitle(address);
TitleChanged?.Invoke(address);
}
AutoFind();
}
private void Control_LoadFailed(int errorCode, string errorText, bool isMainRequest, string url)
@@ -834,14 +856,6 @@ namespace SafeExamBrowser.Browser
}
}
private void AutoFind()
{
if (settings.AllowFind && !string.IsNullOrEmpty(findParameters.term) && !CLEAR_FIND_TERM.Equals(findParameters.term, StringComparison.OrdinalIgnoreCase))
{
Control.Find(findParameters.term, findParameters.isInitial, findParameters.caseSensitive, findParameters.forward);
}
}
private double CalculateZoomPercentage()
{
return (zoomLevel * 25.0) + 100.0;

View File

@@ -30,7 +30,7 @@ namespace SafeExamBrowser.Browser
this.settings = settings;
}
internal void Process(JavascriptMessageReceivedEventArgs message)
internal void Update(JavascriptMessageReceivedEventArgs message)
{
if (settings.UseIsolatedClipboard)
{
@@ -65,7 +65,7 @@ namespace SafeExamBrowser.Browser
private class Data
{
public string Content { get; set; }
public long Id { get; set; }
public string Id { get; set; }
public string Type { get; set; }
}
}

View File

@@ -5,191 +5,207 @@
* 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/.
*
* Original code taken and slightly adapted from https://github.com/eqsoft/seb2/blob/master/browser/app/modules/SebBrowser.jsm#L1215.
* Original code taken and adapted from https://github.com/eqsoft/seb2/blob/master/browser/app/modules/SebBrowser.jsm#L1215.
*/
SafeExamBrowser.clipboard = {
id: Math.round((Date.now() + Math.random()) * 1000),
ranges: [],
text: "",
clear: function () {
this.ranges = [];
this.text = "";
},
getContentEncoded: function () {
var bytes = new TextEncoder().encode(this.text);
var base64 = btoa(String.fromCodePoint(...bytes));
return base64;
},
update: function (id, base64) {
if (this.id != id) {
var bytes = Uint8Array.from(atob(base64), (m) => m.codePointAt(0));
var content = new TextDecoder().decode(bytes);
if (typeof SafeExamBrowser.clipboard === 'undefined') {
SafeExamBrowser.clipboard = {
id: crypto.randomUUID(),
ranges: [],
text: "",
clear: function () {
this.ranges = [];
this.text = content;
}
}
}
this.text = "";
},
function copySelectedData(e) {
if (e.target.contentEditable && e.target.setRangeText) {
SafeExamBrowser.clipboard.text = e.target.value.substring(e.target.selectionStart, e.target.selectionEnd);
SafeExamBrowser.clipboard.ranges = [];
} else {
var selection = e.target.ownerDocument.defaultView.getSelection();
var text = "";
getContentEncoded: function () {
var bytes = new TextEncoder().encode(this.text);
var base64 = btoa(String.fromCodePoint(...bytes));
for (var i = 0; i < selection.rangeCount; i++) {
SafeExamBrowser.clipboard.ranges[i] = selection.getRangeAt(i).cloneContents();
text += SafeExamBrowser.clipboard.ranges[i].textContent;
}
return base64;
},
SafeExamBrowser.clipboard.text = text;
}
}
update: function (id, base64) {
if (this.id != id) {
var bytes = Uint8Array.from(atob(base64), (m) => m.codePointAt(0));
var content = new TextDecoder().decode(bytes);
function cutSelectedData(e) {
if (e.target.contentEditable && e.target.setRangeText) {
e.target.setRangeText("", e.target.selectionStart, e.target.selectionEnd, 'select');
} else {
var designMode = e.target.ownerDocument.designMode;
var contentEditables = e.target.ownerDocument.querySelectorAll('*[contenteditable]');
var selection = e.target.ownerDocument.defaultView.getSelection();
for (var i = 0; i < selection.rangeCount; i++) {
var range = selection.getRangeAt(i);
if (designMode === 'on') {
range.deleteContents();
} else {
if (contentEditables.length) {
contentEditables.forEach(node => {
if (node.contains(range.commonAncestorContainer)) {
range.deleteContents();
}
});
}
this.ranges = [];
this.text = content;
}
}
}
}
function pasteSelectedData(e) {
if (e.target.contentEditable && e.target.setRangeText) {
e.target.setRangeText("", e.target.selectionStart, e.target.selectionEnd, 'select');
e.target.setRangeText(SafeExamBrowser.clipboard.text, e.target.selectionStart, e.target.selectionStart + SafeExamBrowser.clipboard.text.length, 'end');
} else {
var w = e.target.ownerDocument.defaultView;
var designMode = e.target.ownerDocument.designMode;
var contentEditables = e.target.ownerDocument.querySelectorAll('*[contenteditable]');
var selection = w.getSelection();
for (var i = 0; i < selection.rangeCount; i++) {
var r = selection.getRangeAt(i);
if (designMode === 'on') {
r.deleteContents();
} else {
if (contentEditables.length) {
contentEditables.forEach(node => {
if (node.contains(r.commonAncestorContainer)) {
r.deleteContents();
}
});
}
}
}
if (designMode === 'on') {
var range = w.getSelection().getRangeAt(0);
if (SafeExamBrowser.clipboard.ranges.length > 0) {
SafeExamBrowser.clipboard.ranges.map(r => {
range = w.getSelection().getRangeAt(0);
range.collapse();
const newNode = r.cloneNode(true);
range.insertNode(newNode);
range.collapse();
});
} else {
range.collapse();
range.insertNode(w.document.createTextNode(SafeExamBrowser.clipboard.text));
range.collapse();
}
if (typeof copySelection === 'undefined') {
function copySelection(e) {
if (e.target.contentEditable && e.target.setRangeText) {
SafeExamBrowser.clipboard.text = e.target.value.substring(e.target.selectionStart, e.target.selectionEnd);
SafeExamBrowser.clipboard.ranges = [];
} else {
if (contentEditables.length) {
contentEditables.forEach(node => {
var range = w.getSelection().getRangeAt(0);
var selection = e.target.ownerDocument.defaultView.getSelection();
var text = "";
if (node.contains(range.commonAncestorContainer)) {
if (SafeExamBrowser.clipboard.ranges.length > 0) {
SafeExamBrowser.clipboard.ranges.map(r => {
range = w.getSelection().getRangeAt(0);
range.collapse();
const newNode = r.cloneNode(true);
range.insertNode(newNode);
range.collapse();
});
} else {
range = w.getSelection().getRangeAt(0);
range.collapse();
range.insertNode(w.document.createTextNode(SafeExamBrowser.clipboard.text));
range.collapse();
}
for (var i = 0; i < selection.rangeCount; i++) {
SafeExamBrowser.clipboard.ranges[i] = selection.getRangeAt(i).cloneContents();
text += SafeExamBrowser.clipboard.ranges[i].textContent;
}
SafeExamBrowser.clipboard.text = text;
}
}
}
if (typeof cutSelection === 'undefined') {
function cutSelection(e) {
if (e.target.contentEditable && e.target.setRangeText) {
e.target.setRangeText("", e.target.selectionStart, e.target.selectionEnd, 'select');
} else {
var designMode = e.target.ownerDocument.designMode;
var contentEditables = e.target.ownerDocument.querySelectorAll('*[contenteditable]');
var selection = e.target.ownerDocument.defaultView.getSelection();
for (var i = 0; i < selection.rangeCount; i++) {
var range = selection.getRangeAt(i);
if (designMode === 'on') {
range.deleteContents();
} else {
if (contentEditables.length) {
contentEditables.forEach(node => {
if (node.contains(range.commonAncestorContainer)) {
range.deleteContents();
}
});
}
});
}
}
}
}
}
function onCopy(e) {
SafeExamBrowser.clipboard.clear();
if (typeof pasteContent === 'undefined') {
function pasteContent(e) {
if (e.target.contentEditable && e.target.setRangeText) {
e.target.setRangeText("", e.target.selectionStart, e.target.selectionEnd, 'select');
e.target.setRangeText(SafeExamBrowser.clipboard.text, e.target.selectionStart, e.target.selectionStart + SafeExamBrowser.clipboard.text.length, 'end');
} else {
var targetWindow = e.target.ownerDocument.defaultView;
var designMode = e.target.ownerDocument.designMode;
var contentEditables = e.target.ownerDocument.querySelectorAll('*[contenteditable]');
var selection = targetWindow.getSelection();
try {
copySelectedData(e);
for (var i = 0; i < selection.rangeCount; i++) {
var r = selection.getRangeAt(i);
CefSharp.PostMessage({ Type: "Clipboard", Id: SafeExamBrowser.clipboard.id, Content: SafeExamBrowser.clipboard.getContentEncoded() });
} finally {
e.preventDefault();
e.returnValue = false;
if (designMode === 'on') {
r.deleteContents();
} else {
if (contentEditables.length) {
contentEditables.forEach(node => {
if (node.contains(r.commonAncestorContainer)) {
r.deleteContents();
}
});
}
}
}
if (designMode === 'on') {
var range = targetWindow.getSelection().getRangeAt(0);
if (SafeExamBrowser.clipboard.ranges.length > 0) {
SafeExamBrowser.clipboard.ranges.map(r => {
range = targetWindow.getSelection().getRangeAt(0);
range.collapse();
const newNode = r.cloneNode(true);
range.insertNode(newNode);
range.collapse();
});
} else {
range.collapse();
range.insertNode(targetWindow.document.createTextNode(SafeExamBrowser.clipboard.text));
range.collapse();
}
} else {
if (contentEditables.length) {
contentEditables.forEach(node => {
var range = targetWindow.getSelection().getRangeAt(0);
if (node.contains(range.commonAncestorContainer)) {
if (SafeExamBrowser.clipboard.ranges.length > 0) {
SafeExamBrowser.clipboard.ranges.map(r => {
range = targetWindow.getSelection().getRangeAt(0);
range.collapse();
const newNode = r.cloneNode(true);
range.insertNode(newNode);
range.collapse();
});
} else {
range = targetWindow.getSelection().getRangeAt(0);
range.collapse();
range.insertNode(targetWindow.document.createTextNode(SafeExamBrowser.clipboard.text));
range.collapse();
}
}
});
}
}
}
}
return false;
}
function onCut(e) {
SafeExamBrowser.clipboard.clear();
if (typeof onCopy === 'undefined') {
function onCopy(e) {
try {
SafeExamBrowser.clipboard.clear();
try {
copySelectedData(e);
cutSelectedData(e);
copySelection(e);
CefSharp.PostMessage({ Type: "Clipboard", Id: SafeExamBrowser.clipboard.id, Content: SafeExamBrowser.clipboard.getContentEncoded() });
} finally {
e.preventDefault();
e.returnValue = false;
CefSharp.PostMessage({ Type: "Clipboard", Id: SafeExamBrowser.clipboard.id, Content: SafeExamBrowser.clipboard.getContentEncoded() });
} finally {
e.preventDefault();
}
return false;
}
return false;
window.document.addEventListener("copy", onCopy, true);
}
function onPaste(e) {
try {
pasteSelectedData(e);
} finally {
e.preventDefault();
e.returnValue = false;
if (typeof onCut === 'undefined') {
function onCut(e) {
try {
SafeExamBrowser.clipboard.clear();
copySelection(e);
cutSelection(e);
CefSharp.PostMessage({ Type: "Clipboard", Id: SafeExamBrowser.clipboard.id, Content: SafeExamBrowser.clipboard.getContentEncoded() });
} finally {
e.preventDefault();
}
return false;
}
return false;
window.document.addEventListener("cut", onCut, true);
}
window.document.addEventListener("copy", onCopy, true);
window.document.addEventListener("cut", onCut, true);
window.document.addEventListener("paste", onPaste, true);
if (typeof onPaste === 'undefined') {
function onPaste(e) {
try {
pasteContent(e);
} finally {
e.preventDefault();
}
return false;
}
window.document.addEventListener("paste", onPaste, true);
}

View File

@@ -8,5 +8,5 @@
namespace SafeExamBrowser.Browser.Events
{
internal delegate void ClipboardChangedEventHandler(long id);
internal delegate void ClipboardChangedEventHandler(string id);
}

View File

@@ -12,21 +12,21 @@ namespace SafeExamBrowser.Browser.Handlers
{
internal class ContextMenuHandler : IContextMenuHandler
{
public void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model)
public void OnBeforeContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model)
{
model.Clear();
}
public bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags)
public bool OnContextMenuCommand(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags)
{
return false;
}
public void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame)
public void OnContextMenuDismissed(IWebBrowser webBrowser, IBrowser browser, IFrame frame)
{
}
public bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback)
public bool RunContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback)
{
return false;
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2025 ETH Zürich, IT Services
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System.Collections.Generic;
using System.Threading.Tasks;
using CefSharp;
using SafeExamBrowser.Browser.Contracts.Events;
using SafeExamBrowser.Browser.Integrations;
namespace SafeExamBrowser.Browser.Handlers
{
internal class CookieVisitor : ICookieVisitor
{
private readonly IEnumerable<Integration> integrations;
internal event UserIdentifierDetectedEventHandler UserIdentifierDetected;
internal CookieVisitor(IEnumerable<Integration> integrations)
{
this.integrations = integrations;
}
public void Dispose()
{
}
public bool Visit(Cookie cookie, int count, int total, ref bool deleteCookie)
{
foreach (var integration in integrations)
{
var success = integration.TrySearchUserIdentifier(cookie, out var userIdentifier);
if (success)
{
Task.Run(() => UserIdentifierDetected?.Invoke(userIdentifier));
break;
}
}
return true;
}
}
}

View File

@@ -16,7 +16,7 @@ namespace SafeExamBrowser.Browser.Handlers
{
public bool OnDragEnter(IWebBrowser chromiumWebBrowser, IBrowser browser, IDragData dragData, DragOperationsMask mask)
{
return true;
return !(dragData.IsFragment && mask.HasFlag(DragOperationsMask.Move));
}
public void OnDraggableRegionsChanged(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IList<DraggableRegion> regions)

View File

@@ -7,18 +7,16 @@
*/
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Mime;
using System.Threading.Tasks;
using CefSharp;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SafeExamBrowser.Browser.Content;
using SafeExamBrowser.Browser.Contracts.Events;
using SafeExamBrowser.Browser.Contracts.Filters;
using SafeExamBrowser.Browser.Integrations;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.I18n.Contracts;
@@ -36,6 +34,7 @@ namespace SafeExamBrowser.Browser.Handlers
private readonly AppConfig appConfig;
private readonly ContentLoader contentLoader;
private readonly IRequestFilter filter;
private readonly IEnumerable<Integration> integrations;
private readonly IKeyGenerator keyGenerator;
private readonly ILogger logger;
private readonly SessionMode sessionMode;
@@ -44,13 +43,13 @@ namespace SafeExamBrowser.Browser.Handlers
private IResourceHandler contentHandler;
private IResourceHandler pageHandler;
private string userIdentifier;
internal event UserIdentifierDetectedEventHandler UserIdentifierDetected;
internal ResourceHandler(
AppConfig appConfig,
IRequestFilter filter,
IEnumerable<Integration> integrations,
IKeyGenerator keyGenerator,
ILogger logger,
SessionMode sessionMode,
@@ -60,6 +59,7 @@ namespace SafeExamBrowser.Browser.Handlers
{
this.appConfig = appConfig;
this.filter = filter;
this.integrations = integrations;
this.contentLoader = new ContentLoader(text);
this.keyGenerator = keyGenerator;
this.logger = logger;
@@ -235,217 +235,17 @@ namespace SafeExamBrowser.Browser.Handlers
private void SearchUserIdentifier(IRequest request, IResponse response)
{
var success = TrySearchGenericUserIdentifier(response);
if (!success)
foreach (var integration in integrations)
{
success = TrySearchEdxUserIdentifier(response);
}
var success = integration.TrySearchUserIdentifier(request, response, out var userIdentifier);
if (!success)
{
TrySearchMoodleUserIdentifier(request, response);
}
}
private bool TrySearchGenericUserIdentifier(IResponse response)
{
var ids = response.Headers.GetValues("X-LMS-USER-ID");
var success = false;
if (ids != default(string[]))
{
var userId = ids.FirstOrDefault();
if (userId != default && userIdentifier != userId)
if (success)
{
userIdentifier = userId;
Task.Run(() => UserIdentifierDetected?.Invoke(userIdentifier));
logger.Info("Generic LMS user identifier detected.");
success = true;
break;
}
}
return success;
}
private bool TrySearchEdxUserIdentifier(IResponse response)
{
var cookies = response.Headers.GetValues("Set-Cookie");
var success = false;
if (cookies != default(string[]))
{
try
{
var userInfo = cookies.FirstOrDefault(c => c.Contains("edx-user-info"));
if (userInfo != default)
{
var start = userInfo.IndexOf("=") + 1;
var end = userInfo.IndexOf("; expires");
var cookie = userInfo.Substring(start, end - start);
var sanitized = cookie.Replace("\\\"", "\"").Replace("\\054", ",").Trim('"');
var json = JsonConvert.DeserializeObject(sanitized) as JObject;
var userName = json["username"].Value<string>();
if (userIdentifier != userName)
{
userIdentifier = userName;
Task.Run(() => UserIdentifierDetected?.Invoke(userIdentifier));
logger.Info("EdX user identifier detected.");
success = true;
}
}
}
catch (Exception e)
{
logger.Error("Failed to parse edX user identifier!", e);
}
}
return success;
}
private bool TrySearchMoodleUserIdentifier(IRequest request, IResponse response)
{
var success = TrySearchMoodleUserIdentifierByLocation(response);
if (!success)
{
success = TrySearchMoodleUserIdentifierByRequest(MoodleRequestType.Plugin, request, response);
}
if (!success)
{
success = TrySearchMoodleUserIdentifierByRequest(MoodleRequestType.Theme, request, response);
}
return success;
}
private bool TrySearchMoodleUserIdentifierByLocation(IResponse response)
{
var locations = response.Headers.GetValues("Location");
if (locations != default(string[]))
{
try
{
var location = locations.FirstOrDefault(l => l.Contains("/login/index.php?testsession"));
if (location != default)
{
var userId = location.Substring(location.IndexOf("=") + 1);
if (userIdentifier != userId)
{
userIdentifier = userId;
Task.Run(() => UserIdentifierDetected?.Invoke(userIdentifier));
logger.Info("Moodle user identifier detected by location.");
}
return true;
}
}
catch (Exception e)
{
logger.Error("Failed to parse Moodle user identifier by location!", e);
}
}
return false;
}
private bool TrySearchMoodleUserIdentifierByRequest(MoodleRequestType type, IRequest request, IResponse response)
{
var cookies = response.Headers.GetValues("Set-Cookie");
var success = false;
if (cookies != default(string[]))
{
var session = cookies.FirstOrDefault(c => c.Contains("MoodleSession"));
if (session != default)
{
var userId = ExecuteMoodleUserIdentifierRequest(request.Url, session, type);
if (int.TryParse(userId, out var id) && id > 0 && userIdentifier != userId)
{
userIdentifier = userId;
Task.Run(() => UserIdentifierDetected?.Invoke(userIdentifier));
logger.Info($"Moodle user identifier detected by request ({type}).");
success = true;
}
}
}
return success;
}
private string ExecuteMoodleUserIdentifierRequest(string requestUrl, string session, MoodleRequestType type)
{
var userId = default(string);
try
{
Task.Run(async () =>
{
try
{
var endpointUrl = default(string);
var start = session.IndexOf("=") + 1;
var end = session.IndexOf(";");
var name = session.Substring(0, start - 1);
var value = session.Substring(start, end - start);
var uri = new Uri(requestUrl);
if (type == MoodleRequestType.Plugin)
{
endpointUrl = $"{uri.Scheme}{Uri.SchemeDelimiter}{uri.Host}/mod/quiz/accessrule/sebserver/classes/external/user.php";
}
else
{
endpointUrl = $"{uri.Scheme}{Uri.SchemeDelimiter}{uri.Host}/theme/boost_ethz/sebuser.php";
}
var message = new HttpRequestMessage(HttpMethod.Get, endpointUrl);
using (var handler = new HttpClientHandler { UseCookies = false })
using (var client = new HttpClient(handler))
{
message.Headers.Add("Cookie", $"{name}={value}");
var result = await client.SendAsync(message);
if (result.IsSuccessStatusCode)
{
userId = await result.Content.ReadAsStringAsync();
}
else if (result.StatusCode != HttpStatusCode.NotFound)
{
logger.Error($"Failed to retrieve Moodle user identifier by request ({type})! Response: {(int) result.StatusCode} {result.ReasonPhrase}");
}
}
}
catch (Exception e)
{
logger.Error($"Failed to parse Moodle user identifier by request ({type})!", e);
}
}).GetAwaiter().GetResult();
}
catch (Exception e)
{
logger.Error($"Failed to execute Moodle user identifier request ({type})!", e);
}
return userId;
}
private enum MoodleRequestType
{
Plugin,
Theme
}
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) 2025 ETH Zürich, IT Services
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Linq;
using CefSharp;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Browser.Integrations
{
internal class EdxIntegration : Integration
{
private readonly ILogger logger;
public EdxIntegration(ILogger logger)
{
this.logger = logger;
}
internal override bool TrySearchUserIdentifier(Cookie cookie, out string userIdentifier)
{
userIdentifier = default;
return false;
}
internal override bool TrySearchUserIdentifier(IRequest request, IResponse response, out string userIdentifier)
{
var cookies = response.Headers.GetValues("Set-Cookie");
var userInfo = cookies?.FirstOrDefault(c => c.Contains("edx-user-info"));
userIdentifier = default;
if (TryParseCookie(userInfo, out var id) && HasChanged(id))
{
userIdentifier = id;
logger.Info($"User identifier '{id}' detected by session cookie on response.");
}
return userIdentifier != default;
}
private bool TryParseCookie(string userInfo, out string userIdentifier)
{
userIdentifier = default;
try
{
if (userInfo != default)
{
var start = userInfo.IndexOf("=") + 1;
var end = userInfo.IndexOf("; expires");
var cookie = userInfo.Substring(start, end - start);
var sanitized = cookie.Replace("\\\"", "\"").Replace("\\054", ",").Trim('"');
var json = JsonConvert.DeserializeObject(sanitized) as JObject;
userIdentifier = json["username"].Value<string>();
}
}
catch (Exception e)
{
logger.Error("Failed to parse user identifier!", e);
}
return userIdentifier != default;
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2025 ETH Zürich, IT Services
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System.Linq;
using CefSharp;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Browser.Integrations
{
internal class GenericIntegration : Integration
{
private readonly ILogger logger;
public GenericIntegration(ILogger logger)
{
this.logger = logger;
}
internal override bool TrySearchUserIdentifier(Cookie cookie, out string userIdentifier)
{
userIdentifier = default;
return false;
}
internal override bool TrySearchUserIdentifier(IRequest request, IResponse response, out string userIdentifier)
{
var ids = response.Headers.GetValues("X-LMS-USER-ID");
var id = ids?.FirstOrDefault();
userIdentifier = default;
if (HasChanged(id))
{
userIdentifier = id;
logger.Info($"User identifier '{id}' detected by header of response.");
}
return userIdentifier != default;
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2025 ETH Zürich, IT Services
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using CefSharp;
namespace SafeExamBrowser.Browser.Integrations
{
internal abstract class Integration
{
private static string activeUserIdentifier;
internal abstract bool TrySearchUserIdentifier(Cookie cookie, out string userIdentifier);
internal abstract bool TrySearchUserIdentifier(IRequest request, IResponse response, out string userIdentifier);
protected bool HasChanged(string userIdentifier)
{
var current = activeUserIdentifier;
if (userIdentifier != default && activeUserIdentifier != userIdentifier)
{
activeUserIdentifier = userIdentifier;
}
return activeUserIdentifier != current;
}
}
}

View File

@@ -0,0 +1,226 @@
/*
* Copyright (c) 2025 ETH Zürich, IT Services
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using CefSharp;
using SafeExamBrowser.Logging.Contracts;
using Cookie = CefSharp.Cookie;
namespace SafeExamBrowser.Browser.Integrations
{
internal class MoodleIntegration : Integration
{
private const string PLUGIN_PATH = "/mod/quiz/accessrule/sebserver/classes/external/user.php";
private const string SESSION_COOKIE_NAME = "MoodleSession";
private const string THEME_PATH = "/theme/boost_ethz/sebuser.php";
private readonly ILogger logger;
public MoodleIntegration(ILogger logger)
{
this.logger = logger;
}
internal override bool TrySearchUserIdentifier(Cookie cookie, out string userIdentifier)
{
return TrySearchByCookie(cookie, out userIdentifier);
}
internal override bool TrySearchUserIdentifier(IRequest request, IResponse response, out string userIdentifier)
{
var success = TrySearchByLocation(response, out userIdentifier);
if (!success)
{
success = TrySearchByRequests(request, response, out userIdentifier);
}
return success;
}
private bool TrySearchByCookie(Cookie cookie, out string userIdentifier)
{
var id = default(string);
var type = default(RequestType);
var isSession = cookie.Name.Contains(SESSION_COOKIE_NAME);
var url = $"{(cookie.Secure ? Uri.UriSchemeHttps : Uri.UriSchemeHttp)}{Uri.SchemeDelimiter}{cookie.Domain}{cookie.Path}";
var hasId = isSession && TryExecuteRequests(url, (cookie.Name, cookie.Value), out type, out id);
userIdentifier = default;
if (hasId && HasChanged(id))
{
userIdentifier = id;
logger.Info($"User identifier '{id}' detected by request on cookie traversal ({type}).");
}
return userIdentifier != default;
}
private bool TrySearchByLocation(IResponse response, out string userIdentifier)
{
var locations = response.Headers.GetValues("Location");
var location = locations?.FirstOrDefault(l => l.Contains("/login/index.php?testsession"));
userIdentifier = default;
if (TryParseLocation(location, out var id) && HasChanged(id))
{
userIdentifier = id;
logger.Info($"User identifier '{id}' detected by location header of response.");
}
return userIdentifier != default;
}
private bool TrySearchByRequests(IRequest request, IResponse response, out string userIdentifier)
{
var id = default(string);
var type = default(RequestType);
var cookies = response.Headers.GetValues("Set-Cookie");
var session = cookies?.FirstOrDefault(c => c.Contains(SESSION_COOKIE_NAME));
var hasCookie = TryParseCookie(session, out var cookie);
var hasId = hasCookie && TryExecuteRequests(request.Url, cookie, out type, out id);
userIdentifier = default;
if (hasId && HasChanged(id))
{
userIdentifier = id;
logger.Info($"User identifier '{id}' detected by request on response ({type}).");
}
return userIdentifier != default;
}
private bool TryExecuteRequests(string originUrl, (string name, string value) session, out RequestType requestType, out string userId)
{
var order = new[] { RequestType.Plugin, RequestType.Theme };
requestType = default;
userId = default;
foreach (var type in order)
{
try
{
var url = BuildUrl(originUrl, type);
using (var response = ExecuteRequest(url, session))
{
if (TryParseResponse(response, type, out var id))
{
requestType = type;
userId = id;
break;
}
}
}
catch (Exception e)
{
logger.Error($"Failed to execute user identifier request ({type})!", e);
}
}
return userId != default;
}
private string BuildUrl(string originUrl, RequestType type)
{
var uri = new Uri(originUrl);
var endpointUrl = $"{uri.Scheme}{Uri.SchemeDelimiter}{uri.Host}{(type == RequestType.Plugin ? PLUGIN_PATH : THEME_PATH)}";
return endpointUrl;
}
private HttpResponseMessage ExecuteRequest(string url, (string name, string value) session)
{
using (var message = new HttpRequestMessage(HttpMethod.Get, url))
using (var handler = new HttpClientHandler { UseCookies = false })
using (var client = new HttpClient(handler))
{
message.Headers.Add("Cookie", $"{session.name}={session.value}");
return client.SendAsync(message).GetAwaiter().GetResult();
}
}
private bool TryParseCookie(string session, out (string name, string value) cookie)
{
cookie = default;
try
{
if (session != default)
{
var start = session.IndexOf("=") + 1;
var end = session.IndexOf(";");
cookie.name = session.Substring(0, start - 1);
cookie.value = session.Substring(start, end - start);
}
}
catch (Exception e)
{
logger.Error("Failed to parse session cookie!", e);
}
return cookie.name != default && cookie.value != default;
}
private bool TryParseLocation(string location, out string userId)
{
userId = default;
try
{
if (location != default)
{
userId = location.Substring(location.IndexOf("=") + 1);
}
}
catch (Exception e)
{
logger.Error("Failed to parse location!", e);
}
return userId != default;
}
private bool TryParseResponse(HttpResponseMessage response, RequestType type, out string userId)
{
userId = default;
if (response.IsSuccessStatusCode)
{
var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
if (int.TryParse(content, out var id) && id > 0)
{
userId = content;
}
}
else if (response.StatusCode != HttpStatusCode.NotFound)
{
logger.Error($"Failed to retrieve user identifier by request ({type})! Response: {(int) response.StatusCode} {response.ReasonPhrase}");
}
return userId != default;
}
private enum RequestType
{
Plugin,
Theme
}
}
}

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.props" Condition="Exists('..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.props')" />
<Import Project="..\packages\chromiumembeddedframework.runtime.win-x86.131.3.5\build\chromiumembeddedframework.runtime.win-x86.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x86.131.3.5\build\chromiumembeddedframework.runtime.win-x86.props')" />
<Import Project="..\packages\chromiumembeddedframework.runtime.win-x64.131.3.5\build\chromiumembeddedframework.runtime.win-x64.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x64.131.3.5\build\chromiumembeddedframework.runtime.win-x64.props')" />
<Import Project="..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.props" Condition="Exists('..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.props')" />
<Import Project="..\packages\chromiumembeddedframework.runtime.win-x86.139.0.28\build\chromiumembeddedframework.runtime.win-x86.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x86.139.0.28\build\chromiumembeddedframework.runtime.win-x86.props')" />
<Import Project="..\packages\chromiumembeddedframework.runtime.win-x64.139.0.28\build\chromiumembeddedframework.runtime.win-x64.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x64.139.0.28\build\chromiumembeddedframework.runtime.win-x64.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -53,14 +53,14 @@
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<ItemGroup>
<Reference Include="CefSharp, Version=131.3.50.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
<HintPath>..\packages\CefSharp.Common.131.3.50\lib\net462\CefSharp.dll</HintPath>
<Reference Include="CefSharp, Version=139.0.280.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
<HintPath>..\packages\CefSharp.Common.139.0.280\lib\net462\CefSharp.dll</HintPath>
</Reference>
<Reference Include="CefSharp.Core, Version=131.3.50.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
<HintPath>..\packages\CefSharp.Common.131.3.50\lib\net462\CefSharp.Core.dll</HintPath>
<Reference Include="CefSharp.Core, Version=139.0.280.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
<HintPath>..\packages\CefSharp.Common.139.0.280\lib\net462\CefSharp.Core.dll</HintPath>
</Reference>
<Reference Include="CefSharp.WinForms, Version=131.3.50.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
<HintPath>..\packages\CefSharp.WinForms.131.3.50\lib\net462\CefSharp.WinForms.dll</HintPath>
<Reference Include="CefSharp.WinForms, Version=139.0.280.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
<HintPath>..\packages\CefSharp.WinForms.139.0.280\lib\net462\CefSharp.WinForms.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
@@ -103,6 +103,7 @@
<Compile Include="Handlers\ContextMenuHandler.cs" />
<Compile Include="BrowserControl.cs" />
<Compile Include="BrowserIconResource.cs" />
<Compile Include="Handlers\CookieVisitor.cs" />
<Compile Include="Handlers\DialogHandler.cs" />
<Compile Include="Handlers\DisplayHandler.cs" />
<Compile Include="Handlers\DownloadHandler.cs" />
@@ -113,6 +114,10 @@
<Compile Include="Handlers\RequestHandler.cs" />
<Compile Include="Handlers\ResourceHandler.cs" />
<Compile Include="Content\ContentLoader.cs" />
<Compile Include="Integrations\EdxIntegration.cs" />
<Compile Include="Integrations\GenericIntegration.cs" />
<Compile Include="Integrations\Integration.cs" />
<Compile Include="Integrations\MoodleIntegration.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Wrapper\CefSharpBrowserControl.cs">
<SubType>Component</SubType>
@@ -122,10 +127,13 @@
</Compile>
<Compile Include="Wrapper\Events\AuthCredentialsEventHandler.cs" />
<Compile Include="Wrapper\Events\BeforeBrowseEventHandler.cs" />
<Compile Include="Wrapper\Events\BeforeContextMenuEventHandler.cs" />
<Compile Include="Wrapper\Events\BeforeDownloadEventHandler.cs" />
<Compile Include="Wrapper\Events\BeforeUnloadDialogEventHandler.cs" />
<Compile Include="Wrapper\Events\CanDownloadEventHandler.cs" />
<Compile Include="Wrapper\Events\ContextCreatedEventHandler.cs" />
<Compile Include="Wrapper\Events\ContextMenuCommandEventHandler.cs" />
<Compile Include="Wrapper\Events\ContextMenuDismissedEventHandler.cs" />
<Compile Include="Wrapper\Events\ContextReleasedEventHandler.cs" />
<Compile Include="Wrapper\Events\DialogClosedEventHandler.cs" />
<Compile Include="Wrapper\Events\DownloadUpdatedEventHandler.cs" />
@@ -144,10 +152,12 @@
<Compile Include="Wrapper\Events\ResetDialogStateEventHandler.cs" />
<Compile Include="Wrapper\Events\ResourceRequestEventArgs.cs" />
<Compile Include="Wrapper\Events\ResourceRequestEventHandler.cs" />
<Compile Include="Wrapper\Events\RunContextMenuEventHandler.cs" />
<Compile Include="Wrapper\Events\SetFocusEventHandler.cs" />
<Compile Include="Wrapper\Events\TakeFocusEventHandler.cs" />
<Compile Include="Wrapper\Events\UncaughtExceptionEventHandler.cs" />
<Compile Include="Wrapper\Extensions.cs" />
<Compile Include="Wrapper\Handlers\ContextMenuHandlerSwitch.cs" />
<Compile Include="Wrapper\Handlers\DialogHandlerSwitch.cs" />
<Compile Include="Wrapper\Handlers\DisplayHandlerSwitch.cs" />
<Compile Include="Wrapper\Handlers\DownloadHandlerSwitch.cs" />
@@ -227,10 +237,10 @@
<PropertyGroup>
<ErrorText>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}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\chromiumembeddedframework.runtime.win-x64.131.3.5\build\chromiumembeddedframework.runtime.win-x64.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x64.131.3.5\build\chromiumembeddedframework.runtime.win-x64.props'))" />
<Error Condition="!Exists('..\packages\chromiumembeddedframework.runtime.win-x86.131.3.5\build\chromiumembeddedframework.runtime.win-x86.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x86.131.3.5\build\chromiumembeddedframework.runtime.win-x86.props'))" />
<Error Condition="!Exists('..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.props'))" />
<Error Condition="!Exists('..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.targets'))" />
<Error Condition="!Exists('..\packages\chromiumembeddedframework.runtime.win-x64.139.0.28\build\chromiumembeddedframework.runtime.win-x64.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x64.139.0.28\build\chromiumembeddedframework.runtime.win-x64.props'))" />
<Error Condition="!Exists('..\packages\chromiumembeddedframework.runtime.win-x86.139.0.28\build\chromiumembeddedframework.runtime.win-x86.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x86.139.0.28\build\chromiumembeddedframework.runtime.win-x86.props'))" />
<Error Condition="!Exists('..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.props'))" />
<Error Condition="!Exists('..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.targets'))" />
</Target>
<Import Project="..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.targets" Condition="Exists('..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.targets')" />
<Import Project="..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.targets" Condition="Exists('..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.targets')" />
</Project>

View File

@@ -10,7 +10,6 @@ using System.Collections.Generic;
using CefSharp;
using CefSharp.Enums;
using CefSharp.WinForms;
using SafeExamBrowser.Browser.Handlers;
using SafeExamBrowser.Browser.Wrapper.Events;
using SafeExamBrowser.Browser.Wrapper.Handlers;
@@ -20,10 +19,13 @@ namespace SafeExamBrowser.Browser.Wrapper
{
public event AuthCredentialsEventHandler AuthCredentialsRequired;
public event BeforeBrowseEventHandler BeforeBrowse;
public event BeforeContextMenuEventHandler BeforeContextMenu;
public event BeforeDownloadEventHandler BeforeDownload;
public event BeforeUnloadDialogEventHandler BeforeUnloadDialog;
public event CanDownloadEventHandler CanDownload;
public event ContextCreatedEventHandler ContextCreated;
public event ContextMenuCommandEventHandler ContextMenuCommand;
public event ContextMenuDismissedEventHandler ContextMenuDismissed;
public event ContextReleasedEventHandler ContextReleased;
public event DialogClosedEventHandler DialogClosed;
public event DownloadUpdatedEventHandler DownloadUpdated;
@@ -40,6 +42,7 @@ namespace SafeExamBrowser.Browser.Wrapper
public event PreKeyEventHandler PreKeyEvent;
public event ResetDialogStateEventHandler ResetDialogState;
public event ResourceRequestEventHandler ResourceRequestHandlerRequired;
public event RunContextMenuEventHandler RunContextMenu;
public event SetFocusEventHandler SetFocus;
public event TakeFocusEventHandler TakeFocus;
public event UncaughtExceptionEventHandler UncaughtExceptionEvent;
@@ -54,7 +57,7 @@ namespace SafeExamBrowser.Browser.Wrapper
JsDialogHandler = new JavaScriptDialogHandlerSwitch();
KeyboardHandler = new KeyboardHandlerSwitch();
LifeSpanHandler = lifeSpanHandler;
MenuHandler = new ContextMenuHandler();
MenuHandler = new ContextMenuHandlerSwitch();
RenderProcessMessageHandler = new RenderProcessMessageHandlerSwitch();
RequestHandler = new RequestHandlerSwitch();
}
@@ -79,6 +82,11 @@ namespace SafeExamBrowser.Browser.Wrapper
BeforeBrowse?.Invoke(webBrowser, browser, frame, request, userGesture, isRedirect, args);
}
public void OnBeforeContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model)
{
BeforeContextMenu?.Invoke(webBrowser, browser, frame, parameters, model);
}
public void OnBeforeDownload(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback, GenericEventArgs args)
{
BeforeDownload?.Invoke(webBrowser, browser, downloadItem, callback, args);
@@ -99,6 +107,16 @@ namespace SafeExamBrowser.Browser.Wrapper
ContextCreated?.Invoke(webBrowser, browser, frame);
}
public void OnContextMenuCommand(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags, GenericEventArgs args)
{
ContextMenuCommand?.Invoke(webBrowser, browser, frame, parameters, commandId, eventFlags, args);
}
public void OnContextMenuDismissed(IWebBrowser webBrowser, IBrowser browser, IFrame frame)
{
ContextMenuDismissed?.Invoke(webBrowser, browser, frame);
}
public void OnContextReleased(IWebBrowser webBrowser, IBrowser browser, IFrame frame)
{
ContextReleased?.Invoke(webBrowser, browser, frame);
@@ -174,6 +192,11 @@ namespace SafeExamBrowser.Browser.Wrapper
ResetDialogState?.Invoke(webBrowser, browser);
}
public void OnRunContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback, GenericEventArgs args)
{
RunContextMenu?.Invoke(webBrowser, browser, frame, parameters, model, callback, args);
}
public void OnSetFocus(IWebBrowser webBrowser, IBrowser browser, CefFocusSource source, GenericEventArgs args)
{
SetFocus?.Invoke(webBrowser, browser, source, args);

View File

@@ -18,10 +18,13 @@ namespace SafeExamBrowser.Browser.Wrapper
{
public event AuthCredentialsEventHandler AuthCredentialsRequired;
public event BeforeBrowseEventHandler BeforeBrowse;
public event BeforeContextMenuEventHandler BeforeContextMenu;
public event BeforeDownloadEventHandler BeforeDownload;
public event BeforeUnloadDialogEventHandler BeforeUnloadDialog;
public event CanDownloadEventHandler CanDownload;
public event ContextCreatedEventHandler ContextCreated;
public event ContextMenuCommandEventHandler ContextMenuCommand;
public event ContextMenuDismissedEventHandler ContextMenuDismissed;
public event ContextReleasedEventHandler ContextReleased;
public event DialogClosedEventHandler DialogClosed;
public event DownloadUpdatedEventHandler DownloadUpdated;
@@ -38,6 +41,7 @@ namespace SafeExamBrowser.Browser.Wrapper
public event PreKeyEventHandler PreKeyEvent;
public event ResetDialogStateEventHandler ResetDialogState;
public event ResourceRequestEventHandler ResourceRequestHandlerRequired;
public event RunContextMenuEventHandler RunContextMenu;
public event SetFocusEventHandler SetFocus;
public event TakeFocusEventHandler TakeFocus;
public event UncaughtExceptionEventHandler UncaughtExceptionEvent;
@@ -70,6 +74,11 @@ namespace SafeExamBrowser.Browser.Wrapper
BeforeBrowse?.Invoke(webBrowser, browser, frame, request, userGesture, isRedirect, args);
}
public void OnBeforeContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model)
{
BeforeContextMenu?.Invoke(webBrowser, browser, frame, parameters, model);
}
public void OnBeforeDownload(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback, GenericEventArgs args)
{
BeforeDownload?.Invoke(webBrowser, browser, downloadItem, callback, args);
@@ -90,6 +99,16 @@ namespace SafeExamBrowser.Browser.Wrapper
ContextCreated?.Invoke(webBrowser, browser, frame);
}
public void OnContextMenuCommand(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags, GenericEventArgs args)
{
ContextMenuCommand?.Invoke(webBrowser, browser, frame, parameters, commandId, eventFlags, args);
}
public void OnContextMenuDismissed(IWebBrowser webBrowser, IBrowser browser, IFrame frame)
{
ContextMenuDismissed?.Invoke(webBrowser, browser, frame);
}
public void OnContextReleased(IWebBrowser webBrowser, IBrowser browser, IFrame frame)
{
ContextReleased?.Invoke(webBrowser, browser, frame);
@@ -165,6 +184,11 @@ namespace SafeExamBrowser.Browser.Wrapper
ResetDialogState?.Invoke(webBrowser, browser);
}
public void OnRunContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback, GenericEventArgs args)
{
RunContextMenu?.Invoke(webBrowser, browser, frame, parameters, model, callback, args);
}
public void OnSetFocus(IWebBrowser webBrowser, IBrowser browser, CefFocusSource source, GenericEventArgs args)
{
SetFocus?.Invoke(webBrowser, browser, source, args);

View File

@@ -0,0 +1,14 @@
/*
* Copyright (c) 2025 ETH Zürich, IT Services
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using CefSharp;
namespace SafeExamBrowser.Browser.Wrapper.Events
{
internal delegate void BeforeContextMenuEventHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model);
}

View File

@@ -0,0 +1,14 @@
/*
* Copyright (c) 2025 ETH Zürich, IT Services
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using CefSharp;
namespace SafeExamBrowser.Browser.Wrapper.Events
{
internal delegate void ContextMenuCommandEventHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags, GenericEventArgs args);
}

View File

@@ -0,0 +1,14 @@
/*
* Copyright (c) 2025 ETH Zürich, IT Services
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using CefSharp;
namespace SafeExamBrowser.Browser.Wrapper.Events
{
internal delegate void ContextMenuDismissedEventHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame);
}

View File

@@ -0,0 +1,14 @@
/*
* Copyright (c) 2025 ETH Zürich, IT Services
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using CefSharp;
namespace SafeExamBrowser.Browser.Wrapper.Events
{
internal delegate void RunContextMenuEventHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback, GenericEventArgs args);
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (c) 2025 ETH Zürich, IT Services
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using CefSharp;
using CefSharp.WinForms;
using CefSharp.WinForms.Host;
using SafeExamBrowser.Browser.Wrapper.Events;
namespace SafeExamBrowser.Browser.Wrapper.Handlers
{
internal class ContextMenuHandlerSwitch : IContextMenuHandler
{
public void OnBeforeContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model)
{
var control = default(ICefSharpControl);
if (browser.IsPopup)
{
control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
}
else
{
control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
}
control?.OnBeforeContextMenu(webBrowser, browser, frame, parameters, model);
}
public bool OnContextMenuCommand(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags)
{
var args = new GenericEventArgs();
var control = default(ICefSharpControl);
if (browser.IsPopup)
{
control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
}
else
{
control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
}
control?.OnContextMenuCommand(webBrowser, browser, frame, parameters, commandId, eventFlags, args);
return args.Value;
}
public void OnContextMenuDismissed(IWebBrowser webBrowser, IBrowser browser, IFrame frame)
{
var control = default(ICefSharpControl);
if (browser.IsPopup)
{
control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
}
else
{
control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
}
control?.OnContextMenuDismissed(webBrowser, browser, frame);
}
public bool RunContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback)
{
var args = new GenericEventArgs();
var control = default(ICefSharpControl);
if (browser.IsPopup)
{
control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
}
else
{
control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
}
control?.OnRunContextMenu(webBrowser, browser, frame, parameters, model, callback, args);
return args.Value;
}
}
}

View File

@@ -21,10 +21,13 @@ namespace SafeExamBrowser.Browser.Wrapper
{
event AuthCredentialsEventHandler AuthCredentialsRequired;
event BeforeBrowseEventHandler BeforeBrowse;
event BeforeContextMenuEventHandler BeforeContextMenu;
event BeforeDownloadEventHandler BeforeDownload;
event BeforeUnloadDialogEventHandler BeforeUnloadDialog;
event CanDownloadEventHandler CanDownload;
event ContextCreatedEventHandler ContextCreated;
event ContextMenuCommandEventHandler ContextMenuCommand;
event ContextMenuDismissedEventHandler ContextMenuDismissed;
event ContextReleasedEventHandler ContextReleased;
event DialogClosedEventHandler DialogClosed;
event DownloadUpdatedEventHandler DownloadUpdated;
@@ -41,6 +44,7 @@ namespace SafeExamBrowser.Browser.Wrapper
event PreKeyEventHandler PreKeyEvent;
event ResetDialogStateEventHandler ResetDialogState;
event ResourceRequestEventHandler ResourceRequestHandlerRequired;
event RunContextMenuEventHandler RunContextMenu;
event SetFocusEventHandler SetFocus;
event TakeFocusEventHandler TakeFocus;
event UncaughtExceptionEventHandler UncaughtExceptionEvent;
@@ -50,10 +54,13 @@ namespace SafeExamBrowser.Browser.Wrapper
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 OnBeforeContextMenu(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model);
void OnBeforeDownload(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback, GenericEventArgs args);
void OnBeforeUnloadDialog(IWebBrowser webBrowser, IBrowser browser, string message, bool isReload, IJsDialogCallback callback, GenericEventArgs args);
void OnCanDownload(IWebBrowser webBrowser, IBrowser browser, string url, string requestMethod, GenericEventArgs args);
void OnContextCreated(IWebBrowser webBrowser, IBrowser browser, IFrame frame);
void OnContextMenuCommand(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags, GenericEventArgs args);
void OnContextMenuDismissed(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame);
void OnContextReleased(IWebBrowser webBrowser, IBrowser browser, IFrame frame);
void OnDialogClosed(IWebBrowser webBrowser, IBrowser browser);
void OnDownloadUpdated(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback);
@@ -69,6 +76,7 @@ namespace SafeExamBrowser.Browser.Wrapper
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);
void OnResetDialogState(IWebBrowser webBrowser, IBrowser browser);
void OnRunContextMenu(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback, GenericEventArgs args);
void OnSetFocus(IWebBrowser webBrowser, IBrowser browser, CefFocusSource source, GenericEventArgs args);
void OnTakeFocus(IWebBrowser webBrowser, IBrowser browser, bool next);
void OnUncaughtException(IWebBrowser webBrowser, IBrowser browser, IFrame frame, JavascriptException exception);

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CefSharp.Common" version="131.3.50" targetFramework="net48" />
<package id="CefSharp.WinForms" version="131.3.50" targetFramework="net48" />
<package id="chromiumembeddedframework.runtime.win-x64" version="131.3.5" targetFramework="net48" />
<package id="chromiumembeddedframework.runtime.win-x86" version="131.3.5" targetFramework="net48" />
<package id="CefSharp.Common" version="139.0.280" targetFramework="net48" />
<package id="CefSharp.WinForms" version="139.0.280" targetFramework="net48" />
<package id="chromiumembeddedframework.runtime.win-x64" version="139.0.28" targetFramework="net48" />
<package id="chromiumembeddedframework.runtime.win-x86" version="139.0.28" targetFramework="net48" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net48" />
<package id="Syroot.Windows.IO.KnownFolders" version="1.3.0" targetFramework="net48" />
<package id="System.Security.Principal.Windows" version="5.0.0" targetFramework="net48" />