diff --git a/CefSharp.Core.Runtime.RefAssembly/CefSharp.Core.Runtime.netcore.cs b/CefSharp.Core.Runtime.RefAssembly/CefSharp.Core.Runtime.netcore.cs index 98a136f8d0..aa51bd815c 100644 --- a/CefSharp.Core.Runtime.RefAssembly/CefSharp.Core.Runtime.netcore.cs +++ b/CefSharp.Core.Runtime.RefAssembly/CefSharp.Core.Runtime.netcore.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. // GenAPI Version: 5.0.2.37403 @@ -291,6 +291,7 @@ protected virtual void Dispose(bool A_0) { } public virtual System.Threading.Tasks.Task ResolveHostAsync(System.Uri origin) { throw null; } public virtual void SetContentSetting(string requestingUrl, string topLevelUrl, CefSharp.Enums.ContentSettingTypes contentType, CefSharp.Enums.ContentSettingValues value) { } public virtual bool SetPreference(string name, object value, out string error) { throw null; } + public virtual CefSharp.IRegistration AddPreferenceObserver(string name, CefSharp.Callback.IPreferenceObserver observer) { throw null; } public virtual void SetWebsiteSetting(string requestingUrl, string topLevelUrl, CefSharp.Enums.ContentSettingTypes contentType, object value) { } public virtual CefSharp.IRequestContext UnWrap() { throw null; } } diff --git a/CefSharp.Core.Runtime/CefSharp.Core.Runtime.netcore.vcxproj b/CefSharp.Core.Runtime/CefSharp.Core.Runtime.netcore.vcxproj index 036679b913..f074562408 100644 --- a/CefSharp.Core.Runtime/CefSharp.Core.Runtime.netcore.vcxproj +++ b/CefSharp.Core.Runtime/CefSharp.Core.Runtime.netcore.vcxproj @@ -418,6 +418,7 @@ + diff --git a/CefSharp.Core.Runtime/CefSharp.Core.Runtime.netcore.vcxproj.filters b/CefSharp.Core.Runtime/CefSharp.Core.Runtime.netcore.vcxproj.filters index f225404bd0..f98046f0ad 100644 --- a/CefSharp.Core.Runtime/CefSharp.Core.Runtime.netcore.vcxproj.filters +++ b/CefSharp.Core.Runtime/CefSharp.Core.Runtime.netcore.vcxproj.filters @@ -325,6 +325,9 @@ Header Files + + Header Files + diff --git a/CefSharp.Core.Runtime/CefSharp.Core.Runtime.vcxproj b/CefSharp.Core.Runtime/CefSharp.Core.Runtime.vcxproj index 4c7e9e0b69..1c4ef7d507 100644 --- a/CefSharp.Core.Runtime/CefSharp.Core.Runtime.vcxproj +++ b/CefSharp.Core.Runtime/CefSharp.Core.Runtime.vcxproj @@ -317,6 +317,7 @@ + diff --git a/CefSharp.Core.Runtime/CefSharp.Core.Runtime.vcxproj.filters b/CefSharp.Core.Runtime/CefSharp.Core.Runtime.vcxproj.filters index 203ef52396..61665608ce 100644 --- a/CefSharp.Core.Runtime/CefSharp.Core.Runtime.vcxproj.filters +++ b/CefSharp.Core.Runtime/CefSharp.Core.Runtime.vcxproj.filters @@ -319,6 +319,9 @@ Header Files + + Header Files + Header Files diff --git a/CefSharp.Core.Runtime/Internals/PreferenceObserverAdapter.h b/CefSharp.Core.Runtime/Internals/PreferenceObserverAdapter.h new file mode 100644 index 0000000000..221ca88665 --- /dev/null +++ b/CefSharp.Core.Runtime/Internals/PreferenceObserverAdapter.h @@ -0,0 +1,41 @@ +// Copyright © 2026 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +#include "include/cef_preference.h" +#include + +#include "StringUtils.h" + +using namespace CefSharp::Callback; + +namespace CefSharp +{ + namespace Internals + { + private class PreferenceObserverAdapter : public CefPreferenceObserver + { + private: + gcroot _handler; + + public: + PreferenceObserverAdapter(IPreferenceObserver^ handler) + { + _handler = handler; + } + + ~PreferenceObserverAdapter() + { + delete _handler; + _handler = nullptr; + } + + virtual void OnPreferenceChanged(const CefString& name) override + { + _handler->OnPreferenceChanged(StringUtils::ToClr(name)); + } + + IMPLEMENT_REFCOUNTINGM(PreferenceObserverAdapter); + }; + } +} diff --git a/CefSharp.Core.Runtime/RequestContext.cpp b/CefSharp.Core.Runtime/RequestContext.cpp index 0733f69fc4..63d915dfbe 100644 --- a/CefSharp.Core.Runtime/RequestContext.cpp +++ b/CefSharp.Core.Runtime/RequestContext.cpp @@ -11,9 +11,12 @@ #include "Internals\CefSchemeHandlerFactoryAdapter.h" #include "Internals\CefCompletionCallbackAdapter.h" #include "Internals\CefResolveCallbackAdapter.h" +#include "Internals\PreferenceObserverAdapter.h" #include "Internals\TypeConversion.h" +#include "Internals\CefRegistrationWrapper.h" using namespace System::Runtime::InteropServices; +using namespace CefSharp::Callback; namespace CefSharp { @@ -117,6 +120,17 @@ namespace CefSharp return success; } + IRegistration^ RequestContext::AddPreferenceObserver(String^ name, IPreferenceObserver^ observer) + { + ThrowIfDisposed(); + + ThrowIfExecutedOnNonCefUiThread(); + + auto registration = _requestContext->AddPreferenceObserver(StringUtils::ToNative(name), new PreferenceObserverAdapter(observer)); + + return gcnew CefRegistrationWrapper(registration); + } + void RequestContext::ClearCertificateExceptions(ICompletionCallback^ callback) { ThrowIfDisposed(); diff --git a/CefSharp.Core.Runtime/RequestContext.h b/CefSharp.Core.Runtime/RequestContext.h index b54ef157e8..c9714082dd 100644 --- a/CefSharp.Core.Runtime/RequestContext.h +++ b/CefSharp.Core.Runtime/RequestContext.h @@ -16,6 +16,7 @@ using namespace System::Runtime::InteropServices; using namespace System::Threading::Tasks; +using namespace CefSharp::Callback; namespace CefSharp { @@ -254,6 +255,23 @@ namespace CefSharp /// application thread will be the CEF UI thread. virtual bool CanSetPreference(String^ name); + /// + /// Add an observer for preference changes. is the name of the + /// preference to observe. If is empty then all preferences will + /// be observed. Observing all preferences has performance consequences and + /// is not recommended outside of testing scenarios. The observer will remain + /// registered until the returned Registration object is destroyed. This + /// method must be called on the browser process UI thread. + /// + /// preference key + /// preference observer + /// Use Cef.UIThreadTaskFactory to execute this method if required, + /// and ChromiumWebBrowser.IsBrowserInitializedChanged are both + /// executed on the CEF UI thread, so can be called directly. + /// When CefSettings.MultiThreadedMessageLoop == false (the default is true) then the main + /// application thread will be the CEF UI thread. + virtual IRegistration^ AddPreferenceObserver(String^ name, IPreferenceObserver^ observer); + /// /// Set the value associated with preference name. If value is null the /// preference will be restored to its default value. If setting the preference diff --git a/CefSharp.Core/RequestContext.cs b/CefSharp.Core/RequestContext.cs index 5a71fde969..495ecf6e04 100644 --- a/CefSharp.Core/RequestContext.cs +++ b/CefSharp.Core/RequestContext.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using CefSharp.Callback; using CefSharp.Enums; namespace CefSharp @@ -174,6 +175,12 @@ public bool SetPreference(string name, object value, out string error) return requestContext.SetPreference(name, value, out error); } + /// + public IRegistration AddPreferenceObserver(string name, IPreferenceObserver observer) + { + return requestContext.AddPreferenceObserver(name, observer); + } + /// public void ClearCertificateExceptions(ICompletionCallback callback) { diff --git a/CefSharp/Callback/IPreferenceObserver.cs b/CefSharp/Callback/IPreferenceObserver.cs new file mode 100644 index 0000000000..2bbce92292 --- /dev/null +++ b/CefSharp/Callback/IPreferenceObserver.cs @@ -0,0 +1,23 @@ +// Copyright © 2026 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +using System; + +namespace CefSharp.Callback +{ + /// + /// Implemented by the client to observe preference changes and registered via + /// . The methods of this class will + /// be called on the browser process UI thread. + /// + public interface IPreferenceObserver : IDisposable + { + /// + /// Called when a preference has changed. The new value can be retrieved using + /// . + /// + /// preference key + void OnPreferenceChanged(string name); + } +} diff --git a/CefSharp/IRequestContext.cs b/CefSharp/IRequestContext.cs index c912085d7d..a6f0e15a41 100644 --- a/CefSharp/IRequestContext.cs +++ b/CefSharp/IRequestContext.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using CefSharp.Callback; using CefSharp.Enums; namespace CefSharp @@ -158,6 +159,23 @@ public interface IRequestContext : IDisposable /// application thread will be the CEF UI thread. bool SetPreference(string name, object value, out string error); + /// + /// Add an observer for preference changes. is the name of the + /// preference to observe. If is empty then all preferences will + /// be observed. Observing all preferences has performance consequences and + /// is not recommended outside of testing scenarios. The observer will remain + /// registered until the returned Registration object is destroyed. This + /// method must be called on the browser process UI thread. + /// + /// preference key + /// preference observer + /// Use Cef.UIThreadTaskFactory to execute this method if required, + /// and ChromiumWebBrowser.IsBrowserInitializedChanged are both + /// executed on the CEF UI thread, so can be called directly. + /// When CefSettings.MultiThreadedMessageLoop == false (the default is true) then the main + /// application thread will be the CEF UI thread. + IRegistration AddPreferenceObserver(string name, IPreferenceObserver observer); + /// /// Clears all certificate exceptions that were added as part of handling /// . If you call this it is