From eea022da666ec10d7b4e942654bce48a85ef372d Mon Sep 17 00:00:00 2001 From: Chetan Pandey Date: Wed, 15 Apr 2026 14:17:55 +0530 Subject: [PATCH 01/12] diagnostic monitor APi --- specs/DiagnosticMonitor.md | 548 +++++++++++++++++++++++++++++++++++++ 1 file changed, 548 insertions(+) create mode 100644 specs/DiagnosticMonitor.md diff --git a/specs/DiagnosticMonitor.md b/specs/DiagnosticMonitor.md new file mode 100644 index 000000000..bed3aeb88 --- /dev/null +++ b/specs/DiagnosticMonitor.md @@ -0,0 +1,548 @@ +Diagnostic Monitor API +=== + +# Background + +WebView2 host applications today lack a unified way to observe +diagnostic signals — such as network failures — across all WebView +instances and profiles within an environment. Existing APIs such as +`ServerCertificateErrorDetected` are per-WebView or per-profile, +interactive (they expect a response), and each has its own event +shape. + +The Diagnostic Monitor API introduces an observation-only monitor +object that delivers diagnostic signals from all layers — WebView, +Profile, and Environment — through a single `DiagnosticReceived` +event. Host apps create a monitor from the environment and opt in +per category using `AddDiagnosticReceivedFilter`. + + +# Description + +You create an `ICoreWebView2DiagnosticMonitor` from the environment +using `CreateDiagnosticMonitor`. The monitor observes diagnostic +signals across all WebViews, profiles, and the environment itself. +You control which categories of events are delivered by calling +`AddDiagnosticReceivedFilter` with a category and an optional JSON +filter string. + +Key scenarios: + +* **Telemetry** — subscribe to all network errors and forward the + JSON details to your telemetry backend. +* **Targeted monitoring** — filter to specific error codes, HTTP + methods, or profile names using the JSON filter. +* **Multiple consumers** — create separate monitors for telemetry + and a debug panel, each with independent filters. + +The monitor is active from creation until it is released. Releasing +the monitor stops all events and clears all filters automatically. + + +# Examples + +## Subscribe to diagnostic events + +### Win32 C++ + +The following example creates a `DiagnosticMonitor`, adds a filter +for network errors, and subscribes to the `DiagnosticReceived` +event. + +```cpp +class DiagnosticComponent +{ +public: + DiagnosticComponent( + wil::com_ptr environment); + ~DiagnosticComponent(); + +private: + void SetupDiagnostics(); + void HandleDiagnosticEvent( + ICoreWebView2DiagnosticEventArgs* args); + + wil::com_ptr m_environment; + wil::com_ptr m_monitor; + EventRegistrationToken m_diagnosticToken = {}; +}; +``` + +```cpp +DiagnosticComponent::DiagnosticComponent( + wil::com_ptr environment) +{ + CHECK_FAILURE(environment->QueryInterface( + IID_PPV_ARGS(&m_environment))); + SetupDiagnostics(); +} + +DiagnosticComponent::~DiagnosticComponent() +{ + // Releasing the monitor automatically stops all + // events and clears filters. + m_monitor.Reset(); +} + +void DiagnosticComponent::SetupDiagnostics() +{ + // Create a diagnostic monitor from the environment. + CHECK_FAILURE( + m_environment->CreateDiagnosticMonitor(&m_monitor)); + + // Add a filter for NETWORK_ERROR. Pass "{}" to receive + // all network errors without field-level filtering. + CHECK_FAILURE( + m_monitor->AddDiagnosticReceivedFilter( + COREWEBVIEW2_DIAGNOSTIC_CATEGORY_NETWORK_ERROR, + L"{}")); + + // Subscribe to the diagnostic event. + CHECK_FAILURE(m_monitor->add_DiagnosticReceived( + Microsoft::WRL::Callback< + ICoreWebView2DiagnosticReceivedEventHandler>( + [this]( + ICoreWebView2DiagnosticMonitor* sender, + ICoreWebView2DiagnosticEventArgs* args) + -> HRESULT + { + HandleDiagnosticEvent(args); + return S_OK; + }) + .Get(), + &m_diagnosticToken)); +} + +void DiagnosticComponent::HandleDiagnosticEvent( + ICoreWebView2DiagnosticEventArgs* args) +{ + COREWEBVIEW2_DIAGNOSTIC_CATEGORY category; + CHECK_FAILURE(args->get_Category(&category)); + + COREWEBVIEW2_DIAGNOSTIC_SOURCE_SCOPE scope; + CHECK_FAILURE(args->get_Scope(&scope)); + + wil::unique_cotaskmem_string detailsJson; + CHECK_FAILURE(args->GetCategoryDetailsAsJson( + category, &detailsJson)); + + INT64 timestamp = 0; + CHECK_FAILURE(args->get_Timestamp(×tamp)); + + std::wstringstream log; + log << L"[Diagnostic] category=" << category + << L" scope=" << scope + << L" ts=" << timestamp + << L" details=" << detailsJson.get(); + + OutputDebugStringW(log.str().c_str()); +} +``` + +### .NET C# + +```c# +using Microsoft.Web.WebView2.Core; +using System; +using System.Diagnostics; + +public class DiagnosticComponent : IDisposable +{ + private CoreWebView2DiagnosticMonitor _monitor; + + public DiagnosticComponent( + CoreWebView2Environment environment) + { + // Create a diagnostic monitor. + _monitor = environment.CreateDiagnosticMonitor(); + + // Add a filter for NetworkError. Pass "{}" to + // receive all network errors without field-level + // filtering. + _monitor.AddDiagnosticReceivedFilter( + CoreWebView2DiagnosticCategory.NetworkError, + "{}"); + + // Subscribe to the diagnostic event. + _monitor.DiagnosticReceived += + OnDiagnosticReceived; + } + + private void OnDiagnosticReceived( + CoreWebView2DiagnosticMonitor sender, + CoreWebView2DiagnosticEventArgs args) + { + CoreWebView2DiagnosticCategory category = + args.Category; + CoreWebView2DiagnosticSourceScope scope = + args.Scope; + long timestamp = args.Timestamp; + string detailsJson = + args.GetCategoryDetailsAsJson(category); + + Debug.WriteLine( + $"[Diagnostic] category={category} " + + $"scope={scope} ts={timestamp} " + + $"details={detailsJson}"); + } + + public void Dispose() + { + // Disposing the monitor stops all events and + // clears filters automatically. + _monitor?.Dispose(); + } +} +``` + +## Filter with field-level JSON criteria + +You can pass a JSON object to `AddDiagnosticReceivedFilter` to +restrict which events are delivered. An empty JSON `"{}"` receives +all events in that category. A non-empty JSON applies field-level +matching. Calling the method again for the same category replaces +the previous filter. + +### Win32 C++ + +```cpp +void DiagnosticComponent::SetupFilteredDiagnostics() +{ + CHECK_FAILURE( + m_environment->CreateDiagnosticMonitor(&m_monitor)); + + // Only receive DNS and timeout errors (-105, -7) + // for GET/POST requests from the "Default" profile. + CHECK_FAILURE( + m_monitor->AddDiagnosticReceivedFilter( + COREWEBVIEW2_DIAGNOSTIC_CATEGORY_NETWORK_ERROR, + LR"({ + "profileName": "Default", + "errorCode": [-105, -7], + "httpMethod": ["GET", "POST"] + })")); + + CHECK_FAILURE(m_monitor->add_DiagnosticReceived( + Microsoft::WRL::Callback< + ICoreWebView2DiagnosticReceivedEventHandler>( + [this]( + ICoreWebView2DiagnosticMonitor* sender, + ICoreWebView2DiagnosticEventArgs* args) + -> HRESULT + { + HandleDiagnosticEvent(args); + return S_OK; + }) + .Get(), + &m_diagnosticToken)); +} +``` + +### .NET C# + +```c# +private void SetupFilteredDiagnostics() +{ + _monitor = _environment.CreateDiagnosticMonitor(); + + // Only DNS and timeout errors for GET/POST requests + // from the "Default" profile. + _monitor.AddDiagnosticReceivedFilter( + CoreWebView2DiagnosticCategory.NetworkError, + @"{ + ""profileName"": ""Default"", + ""errorCode"": [-105, -7], + ""httpMethod"": [""GET"", ""POST""] + }"); + + _monitor.DiagnosticReceived += + OnDiagnosticReceived; +} +``` + + +# API Details + +## COM + +```idl +/// Specifies the category of diagnostic event. +[v1_enum] +typedef enum COREWEBVIEW2_DIAGNOSTIC_CATEGORY { + /// Network request failure including DNS resolution + /// errors, TLS handshake failures, connection timeouts, + /// HTTP error status codes (4xx/5xx), CORS violations, + /// and mixed-content blocks. + COREWEBVIEW2_DIAGNOSTIC_CATEGORY_NETWORK_ERROR, +} COREWEBVIEW2_DIAGNOSTIC_CATEGORY; + +/// Specifies the scope that originated a diagnostic event. +[v1_enum] +typedef enum COREWEBVIEW2_DIAGNOSTIC_SOURCE_SCOPE { + /// The diagnostic signal originated from a specific + /// WebView instance. + COREWEBVIEW2_DIAGNOSTIC_SOURCE_SCOPE_WEB_VIEW, + + /// The diagnostic signal originated from a profile or + /// its underlying network context but is not tied to a + /// specific WebView. + COREWEBVIEW2_DIAGNOSTIC_SOURCE_SCOPE_PROFILE, + + /// The diagnostic signal originated from the environment + /// (for example, a browser-wide event that affects all + /// WebViews). + COREWEBVIEW2_DIAGNOSTIC_SOURCE_SCOPE_ENVIRONMENT, +} COREWEBVIEW2_DIAGNOSTIC_SOURCE_SCOPE; + +/// Event args for the `DiagnosticReceived` event on +/// `ICoreWebView2DiagnosticMonitor`. Each instance +/// represents a single diagnostic signal. +[uuid(A1B2C3D4-E5F6-7890-ABCD-EF1234567890), + object, pointer_default(unique)] +interface ICoreWebView2DiagnosticEventArgs : IUnknown { + /// The diagnostic category that this event belongs to. + [propget] HRESULT Category( + [out, retval] + COREWEBVIEW2_DIAGNOSTIC_CATEGORY* value); + + /// The scope that originated this diagnostic signal. + [propget] HRESULT Scope( + [out, retval] + COREWEBVIEW2_DIAGNOSTIC_SOURCE_SCOPE* value); + + /// Monotonic timestamp in microseconds since an + /// unspecified epoch. You can use this value to order + /// events but should not convert it to wall-clock time. + [propget] HRESULT Timestamp( + [out, retval] INT64* value); + + /// Returns category-specific diagnostic data as a JSON + /// string for the specified category. + /// + /// The `category` parameter should match the value + /// returned by `get_Category`. If a different category + /// is passed, the method returns `"{}"`. + /// + /// For `COREWEBVIEW2_DIAGNOSTIC_CATEGORY_NETWORK_ERROR` + /// the JSON schema is: + /// ``` + /// { + /// "errorCode": -105, + /// "statusCode": 404, + /// "httpMethod": "GET", + /// "elapsedTime": 1234, + /// "protocol": "https", + /// "uri": "https://www.contoso.com/api/data" + /// } + /// ``` + /// + /// `errorCode` is the Chromium net error code (integer). + /// `statusCode` is the HTTP response status code + /// (integer, 0 if no response was received). + /// `httpMethod` is the HTTP method string. + /// `elapsedTime` is the request duration in + /// milliseconds (integer). + /// `protocol` is the protocol scheme (e.g. "https"). + /// `uri` is the request URI. + /// + /// For categories the runtime does not yet populate, + /// this method returns `"{}"`. + /// + /// The caller must free the returned string with + /// `CoTaskMemFree`. + HRESULT GetCategoryDetailsAsJson( + [in] COREWEBVIEW2_DIAGNOSTIC_CATEGORY category, + [out, retval] LPWSTR* value); +} + +/// Receives `DiagnosticReceived` events from +/// `ICoreWebView2DiagnosticMonitor`. +[uuid(C3D4E5F6-A7B8-9012-CDEF-123456789012), + object, pointer_default(unique)] +interface ICoreWebView2DiagnosticReceivedEventHandler + : IUnknown { + /// Provides the event args for the corresponding event. + HRESULT Invoke( + [in] ICoreWebView2DiagnosticMonitor* sender, + [in] ICoreWebView2DiagnosticEventArgs* args); +} + +/// A diagnostic monitor that receives diagnostic signals +/// from all layers — WebView, Profile, and Environment. +/// +/// Created via +/// `ICoreWebView2Environment17::CreateDiagnosticMonitor`. +/// Each monitor has its own filters and event handlers, +/// allowing multiple independent consumers (for example, +/// one for telemetry, one for a debug panel). +/// +/// The monitor is active from creation until it is +/// released. Releasing the monitor automatically stops all +/// events and clears all filters. +[uuid(E4F5A6B7-C8D9-0123-ABCD-456789012345), + object, pointer_default(unique)] +interface ICoreWebView2DiagnosticMonitor : IUnknown { + + /// Adds a diagnostic filter for the specified category. + /// After this call, `DiagnosticReceived` will fire for + /// events in this category that match the JSON criteria. + /// + /// Pass `"{}"` or an empty string as `jsonFilter` to + /// receive all events in the category without + /// field-level filtering. + /// + /// Pass a JSON object to apply field-level filtering. + /// The object's keys are detail field names. + /// `profileName` is a single string value; all other + /// fields are arrays of accepted values. + /// + /// Example for `NETWORK_ERROR`: + /// ``` + /// { + /// "profileName": "Default", + /// "errorCode": [-105, -7], + /// "statusCode": [404, 500], + /// "uriPattern": ["https://*.contoso.com/*"], + /// "httpMethod": ["GET", "POST"] + /// } + /// ``` + /// + /// `profileName` is a single string that must match + /// the profile name exactly. All other fields are + /// arrays of accepted values. An event passes if it + /// matches **any** value in each specified field + /// (OR within a field, AND across fields). String + /// fields in `uriPattern` support wildcard patterns + /// using `*` and `?`. + /// + /// Calling this method again for the same category + /// replaces the previous filter for that category. + /// + /// Returns `E_INVALIDARG` if the JSON is malformed. + /// On failure the filter state is unchanged. + HRESULT AddDiagnosticReceivedFilter( + [in] COREWEBVIEW2_DIAGNOSTIC_CATEGORY category, + [in] LPCWSTR jsonFilter); + + /// Removes the diagnostic filter for the specified + /// category. After this call, `DiagnosticReceived` + /// will no longer fire for events in this category. + /// + /// If no filter was previously added for the category, + /// this method is a no-op and returns `S_OK`. + HRESULT RemoveDiagnosticReceivedFilter( + [in] COREWEBVIEW2_DIAGNOSTIC_CATEGORY category); + + /// Subscribes to diagnostic events on this monitor. + /// The handler is invoked on the thread that created + /// the environment every time a diagnostic signal + /// passes a filter added with + /// `AddDiagnosticReceivedFilter`. + /// + /// Multiple handlers can be registered. They are + /// invoked in registration order. + HRESULT add_DiagnosticReceived( + [in] ICoreWebView2DiagnosticReceivedEventHandler* + eventHandler, + [out] EventRegistrationToken* token); + + /// Removes a handler previously added with + /// `add_DiagnosticReceived`. + HRESULT remove_DiagnosticReceived( + [in] EventRegistrationToken token); +} + +interface ICoreWebView2Environment17 + : ICoreWebView2Environment16 { + + /// Creates a new diagnostic monitor. The monitor + /// receives diagnostic signals from all layers — + /// WebView, Profile, and Environment — that match its + /// filters. + /// + /// Multiple monitors can coexist, each with its own + /// filters and event handlers. This enables independent + /// consumers such as a telemetry pipeline and a debug + /// panel to operate without interfering with each other. + /// + /// The monitor is active immediately but no events fire + /// until a filter is added via + /// `AddDiagnosticReceivedFilter`. + /// + /// Release the monitor to stop receiving events and + /// free resources. + HRESULT CreateDiagnosticMonitor( + [out, retval] + ICoreWebView2DiagnosticMonitor** value); +} +``` + +## .NET and WinRT + +```c# +namespace Microsoft.Web.WebView2.Core +{ + /// Specifies the category of diagnostic event. + enum CoreWebView2DiagnosticCategory + { + /// Network request failure (DNS, TLS, timeout, + /// HTTP error, CORS, mixed content). + NetworkError = 0, + }; + + /// Specifies the scope that originated a diagnostic + /// event. + enum CoreWebView2DiagnosticSourceScope + { + /// Signal from a specific WebView instance. + WebView = 0, + + /// Signal from a profile / NetworkContext. + Profile = 1, + + /// Signal from the environment. + Environment = 2, + }; + + /// Event args for the DiagnosticReceived event. + runtimeclass CoreWebView2DiagnosticEventArgs + { + CoreWebView2DiagnosticCategory Category { get; }; + CoreWebView2DiagnosticSourceScope Scope { get; }; + Int64 Timestamp { get; }; + + /// Returns category-specific data as a JSON + /// string. Returns "{}" for unrecognized + /// categories. + String GetCategoryDetailsAsJson( + CoreWebView2DiagnosticCategory category); + } + + /// A diagnostic monitor that receives signals from + /// all layers. Implements IClosable for deterministic + /// cleanup. + runtimeclass CoreWebView2DiagnosticMonitor + : Windows.Foundation.IClosable + { + void AddDiagnosticReceivedFilter( + CoreWebView2DiagnosticCategory category, + String jsonFilter); + + void RemoveDiagnosticReceivedFilter( + CoreWebView2DiagnosticCategory category); + + event Windows.Foundation.TypedEventHandler< + CoreWebView2DiagnosticMonitor, + CoreWebView2DiagnosticEventArgs> + DiagnosticReceived; + } + + runtimeclass CoreWebView2Environment + { + // ... + + CoreWebView2DiagnosticMonitor + CreateDiagnosticMonitor(); + } +} +``` From 683ea41af4f738c740b44cd259fbe423441f9ab9 Mon Sep 17 00:00:00 2001 From: Chetan Pandey Date: Mon, 27 Apr 2026 14:16:43 +0530 Subject: [PATCH 02/12] Address review feedback: prose fixes, code sample improvements, and convention alignment - Fix grammar: 'An empty JSON' -> 'An empty JSON object' (2x) - Clarify ambiguity: 'mixed-content blocks' -> 'mixed-content blocked requests' - Use prescriptive language: 'should match' -> 'must match' in API contract - Add missing 'that' in relative clause + imperative voice for CoTaskMemFree - Remove markdown bold (**any**) from IDL comments (plain text only) - Add missing comma after introductory phrase 'On failure' - Split run-on sentence in event handler comment - Add missing comma before 'but' in compound sentence - Remove 'NetworkContext' reference from profile scope comment - C++ destructor: unsubscribe before Reset() for proper cleanup order - C# Dispose: unsubscribe before Dispose() for proper cleanup order - Add _environment field declaration in filtered C# sample - Name opaque error codes: ERR_NAME_NOT_RESOLVED (-105), ERR_TIMED_OUT (-7) - Align section headers: 'COM' -> 'Win32 C++', '.NET C#' -> '.NET, WinRT' Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- specs/DiagnosticMonitor.md | 61 +++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/specs/DiagnosticMonitor.md b/specs/DiagnosticMonitor.md index bed3aeb88..a2e8d3481 100644 --- a/specs/DiagnosticMonitor.md +++ b/specs/DiagnosticMonitor.md @@ -79,9 +79,14 @@ DiagnosticComponent::DiagnosticComponent( DiagnosticComponent::~DiagnosticComponent() { - // Releasing the monitor automatically stops all - // events and clears filters. - m_monitor.Reset(); + if (m_monitor) + { + // Unsubscribe before releasing the monitor to + // ensure no callbacks arrive during teardown. + m_monitor->remove_DiagnosticReceived( + m_diagnosticToken); + m_monitor.Reset(); + } } void DiagnosticComponent::SetupDiagnostics() @@ -139,7 +144,7 @@ void DiagnosticComponent::HandleDiagnosticEvent( } ``` -### .NET C# +### .NET, WinRT ```c# using Microsoft.Web.WebView2.Core; @@ -188,9 +193,14 @@ public class DiagnosticComponent : IDisposable public void Dispose() { - // Disposing the monitor stops all events and - // clears filters automatically. - _monitor?.Dispose(); + if (_monitor != null) + { + // Unsubscribe before disposing the monitor. + _monitor.DiagnosticReceived -= + OnDiagnosticReceived; + _monitor.Dispose(); + _monitor = null; + } } } ``` @@ -198,8 +208,8 @@ public class DiagnosticComponent : IDisposable ## Filter with field-level JSON criteria You can pass a JSON object to `AddDiagnosticReceivedFilter` to -restrict which events are delivered. An empty JSON `"{}"` receives -all events in that category. A non-empty JSON applies field-level +restrict which events are delivered. An empty JSON object `"{}"` receives +all events in that category. A non-empty JSON object applies field-level matching. Calling the method again for the same category replaces the previous filter. @@ -211,7 +221,8 @@ void DiagnosticComponent::SetupFilteredDiagnostics() CHECK_FAILURE( m_environment->CreateDiagnosticMonitor(&m_monitor)); - // Only receive DNS and timeout errors (-105, -7) + // Only receive DNS (ERR_NAME_NOT_RESOLVED, -105) + // and timeout (ERR_TIMED_OUT, -7) errors // for GET/POST requests from the "Default" profile. CHECK_FAILURE( m_monitor->AddDiagnosticReceivedFilter( @@ -238,14 +249,17 @@ void DiagnosticComponent::SetupFilteredDiagnostics() } ``` -### .NET C# +### .NET, WinRT ```c# +private CoreWebView2Environment _environment; + private void SetupFilteredDiagnostics() { _monitor = _environment.CreateDiagnosticMonitor(); - // Only DNS and timeout errors for GET/POST requests + // Only DNS (ERR_NAME_NOT_RESOLVED, -105) and timeout + // (ERR_TIMED_OUT, -7) errors for GET/POST requests // from the "Default" profile. _monitor.AddDiagnosticReceivedFilter( CoreWebView2DiagnosticCategory.NetworkError, @@ -263,7 +277,7 @@ private void SetupFilteredDiagnostics() # API Details -## COM +## Win32 C++ ```idl /// Specifies the category of diagnostic event. @@ -272,7 +286,7 @@ typedef enum COREWEBVIEW2_DIAGNOSTIC_CATEGORY { /// Network request failure including DNS resolution /// errors, TLS handshake failures, connection timeouts, /// HTTP error status codes (4xx/5xx), CORS violations, - /// and mixed-content blocks. + /// and mixed-content blocked requests. COREWEBVIEW2_DIAGNOSTIC_CATEGORY_NETWORK_ERROR, } COREWEBVIEW2_DIAGNOSTIC_CATEGORY; @@ -319,7 +333,7 @@ interface ICoreWebView2DiagnosticEventArgs : IUnknown { /// Returns category-specific diagnostic data as a JSON /// string for the specified category. /// - /// The `category` parameter should match the value + /// The `category` parameter must match the value /// returned by `get_Category`. If a different category /// is passed, the method returns `"{}"`. /// @@ -345,11 +359,10 @@ interface ICoreWebView2DiagnosticEventArgs : IUnknown { /// `protocol` is the protocol scheme (e.g. "https"). /// `uri` is the request URI. /// - /// For categories the runtime does not yet populate, + /// For categories that the runtime does not yet populate, /// this method returns `"{}"`. /// - /// The caller must free the returned string with - /// `CoTaskMemFree`. + /// Free the returned string with `CoTaskMemFree`. HRESULT GetCategoryDetailsAsJson( [in] COREWEBVIEW2_DIAGNOSTIC_CATEGORY category, [out, retval] LPWSTR* value); @@ -410,7 +423,7 @@ interface ICoreWebView2DiagnosticMonitor : IUnknown { /// `profileName` is a single string that must match /// the profile name exactly. All other fields are /// arrays of accepted values. An event passes if it - /// matches **any** value in each specified field + /// matches any value in each specified field /// (OR within a field, AND across fields). String /// fields in `uriPattern` support wildcard patterns /// using `*` and `?`. @@ -419,7 +432,7 @@ interface ICoreWebView2DiagnosticMonitor : IUnknown { /// replaces the previous filter for that category. /// /// Returns `E_INVALIDARG` if the JSON is malformed. - /// On failure the filter state is unchanged. + /// On failure, the filter state is unchanged. HRESULT AddDiagnosticReceivedFilter( [in] COREWEBVIEW2_DIAGNOSTIC_CATEGORY category, [in] LPCWSTR jsonFilter); @@ -435,8 +448,8 @@ interface ICoreWebView2DiagnosticMonitor : IUnknown { /// Subscribes to diagnostic events on this monitor. /// The handler is invoked on the thread that created - /// the environment every time a diagnostic signal - /// passes a filter added with + /// the environment. It fires every time a diagnostic + /// signal passes a filter added with /// `AddDiagnosticReceivedFilter`. /// /// Multiple handlers can be registered. They are @@ -465,7 +478,7 @@ interface ICoreWebView2Environment17 /// consumers such as a telemetry pipeline and a debug /// panel to operate without interfering with each other. /// - /// The monitor is active immediately but no events fire + /// The monitor is active immediately, but no events fire /// until a filter is added via /// `AddDiagnosticReceivedFilter`. /// @@ -497,7 +510,7 @@ namespace Microsoft.Web.WebView2.Core /// Signal from a specific WebView instance. WebView = 0, - /// Signal from a profile / NetworkContext. + /// Signal from a profile. Profile = 1, /// Signal from the environment. From 0894cc0181a10efb3b114cfac41820ad296809a7 Mon Sep 17 00:00:00 2001 From: Chetan Pandey Date: Mon, 27 Apr 2026 14:46:03 +0530 Subject: [PATCH 03/12] PR comments --- specs/DiagnosticMonitor.md | 100 ++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 51 deletions(-) diff --git a/specs/DiagnosticMonitor.md b/specs/DiagnosticMonitor.md index a2e8d3481..ab7296dfa 100644 --- a/specs/DiagnosticMonitor.md +++ b/specs/DiagnosticMonitor.md @@ -14,7 +14,7 @@ The Diagnostic Monitor API introduces an observation-only monitor object that delivers diagnostic signals from all layers — WebView, Profile, and Environment — through a single `DiagnosticReceived` event. Host apps create a monitor from the environment and opt in -per category using `AddDiagnosticReceivedFilter`. +per category using `SetDiagnosticFilter`. # Description @@ -23,7 +23,7 @@ You create an `ICoreWebView2DiagnosticMonitor` from the environment using `CreateDiagnosticMonitor`. The monitor observes diagnostic signals across all WebViews, profiles, and the environment itself. You control which categories of events are delivered by calling -`AddDiagnosticReceivedFilter` with a category and an optional JSON +`SetDiagnosticFilter` with a category and an optional JSON filter string. Key scenarios: @@ -60,7 +60,7 @@ public: private: void SetupDiagnostics(); void HandleDiagnosticEvent( - ICoreWebView2DiagnosticEventArgs* args); + ICoreWebView2DiagnosticReceivedEventArgs* args); wil::com_ptr m_environment; wil::com_ptr m_monitor; @@ -98,7 +98,7 @@ void DiagnosticComponent::SetupDiagnostics() // Add a filter for NETWORK_ERROR. Pass "{}" to receive // all network errors without field-level filtering. CHECK_FAILURE( - m_monitor->AddDiagnosticReceivedFilter( + m_monitor->SetDiagnosticFilter( COREWEBVIEW2_DIAGNOSTIC_CATEGORY_NETWORK_ERROR, L"{}")); @@ -108,7 +108,7 @@ void DiagnosticComponent::SetupDiagnostics() ICoreWebView2DiagnosticReceivedEventHandler>( [this]( ICoreWebView2DiagnosticMonitor* sender, - ICoreWebView2DiagnosticEventArgs* args) + ICoreWebView2DiagnosticReceivedEventArgs* args) -> HRESULT { HandleDiagnosticEvent(args); @@ -119,17 +119,16 @@ void DiagnosticComponent::SetupDiagnostics() } void DiagnosticComponent::HandleDiagnosticEvent( - ICoreWebView2DiagnosticEventArgs* args) + ICoreWebView2DiagnosticReceivedEventArgs* args) { COREWEBVIEW2_DIAGNOSTIC_CATEGORY category; CHECK_FAILURE(args->get_Category(&category)); - COREWEBVIEW2_DIAGNOSTIC_SOURCE_SCOPE scope; + COREWEBVIEW2_DIAGNOSTIC_SCOPE scope; CHECK_FAILURE(args->get_Scope(&scope)); wil::unique_cotaskmem_string detailsJson; - CHECK_FAILURE(args->GetCategoryDetailsAsJson( - category, &detailsJson)); + CHECK_FAILURE(args->GetDetailsAsJson(&detailsJson)); INT64 timestamp = 0; CHECK_FAILURE(args->get_Timestamp(×tamp)); @@ -164,7 +163,7 @@ public class DiagnosticComponent : IDisposable // Add a filter for NetworkError. Pass "{}" to // receive all network errors without field-level // filtering. - _monitor.AddDiagnosticReceivedFilter( + _monitor.SetDiagnosticFilter( CoreWebView2DiagnosticCategory.NetworkError, "{}"); @@ -175,15 +174,15 @@ public class DiagnosticComponent : IDisposable private void OnDiagnosticReceived( CoreWebView2DiagnosticMonitor sender, - CoreWebView2DiagnosticEventArgs args) + CoreWebView2DiagnosticReceivedEventArgs args) { CoreWebView2DiagnosticCategory category = args.Category; - CoreWebView2DiagnosticSourceScope scope = + CoreWebView2DiagnosticScope scope = args.Scope; long timestamp = args.Timestamp; string detailsJson = - args.GetCategoryDetailsAsJson(category); + args.GetDetailsAsJson(); Debug.WriteLine( $"[Diagnostic] category={category} " + @@ -207,7 +206,7 @@ public class DiagnosticComponent : IDisposable ## Filter with field-level JSON criteria -You can pass a JSON object to `AddDiagnosticReceivedFilter` to +You can pass a JSON object to `SetDiagnosticFilter` to restrict which events are delivered. An empty JSON object `"{}"` receives all events in that category. A non-empty JSON object applies field-level matching. Calling the method again for the same category replaces @@ -225,7 +224,7 @@ void DiagnosticComponent::SetupFilteredDiagnostics() // and timeout (ERR_TIMED_OUT, -7) errors // for GET/POST requests from the "Default" profile. CHECK_FAILURE( - m_monitor->AddDiagnosticReceivedFilter( + m_monitor->SetDiagnosticFilter( COREWEBVIEW2_DIAGNOSTIC_CATEGORY_NETWORK_ERROR, LR"({ "profileName": "Default", @@ -238,7 +237,7 @@ void DiagnosticComponent::SetupFilteredDiagnostics() ICoreWebView2DiagnosticReceivedEventHandler>( [this]( ICoreWebView2DiagnosticMonitor* sender, - ICoreWebView2DiagnosticEventArgs* args) + ICoreWebView2DiagnosticReceivedEventArgs* args) -> HRESULT { HandleDiagnosticEvent(args); @@ -261,7 +260,7 @@ private void SetupFilteredDiagnostics() // Only DNS (ERR_NAME_NOT_RESOLVED, -105) and timeout // (ERR_TIMED_OUT, -7) errors for GET/POST requests // from the "Default" profile. - _monitor.AddDiagnosticReceivedFilter( + _monitor.SetDiagnosticFilter( CoreWebView2DiagnosticCategory.NetworkError, @"{ ""profileName"": ""Default"", @@ -292,28 +291,27 @@ typedef enum COREWEBVIEW2_DIAGNOSTIC_CATEGORY { /// Specifies the scope that originated a diagnostic event. [v1_enum] -typedef enum COREWEBVIEW2_DIAGNOSTIC_SOURCE_SCOPE { +typedef enum COREWEBVIEW2_DIAGNOSTIC_SCOPE { /// The diagnostic signal originated from a specific /// WebView instance. - COREWEBVIEW2_DIAGNOSTIC_SOURCE_SCOPE_WEB_VIEW, + COREWEBVIEW2_DIAGNOSTIC_SCOPE_WEB_VIEW, - /// The diagnostic signal originated from a profile or - /// its underlying network context but is not tied to a - /// specific WebView. - COREWEBVIEW2_DIAGNOSTIC_SOURCE_SCOPE_PROFILE, + /// The diagnostic signal originated from a profile + /// but is not tied to a specific WebView. + COREWEBVIEW2_DIAGNOSTIC_SCOPE_PROFILE, /// The diagnostic signal originated from the environment /// (for example, a browser-wide event that affects all /// WebViews). - COREWEBVIEW2_DIAGNOSTIC_SOURCE_SCOPE_ENVIRONMENT, -} COREWEBVIEW2_DIAGNOSTIC_SOURCE_SCOPE; + COREWEBVIEW2_DIAGNOSTIC_SCOPE_ENVIRONMENT, +} COREWEBVIEW2_DIAGNOSTIC_SCOPE; /// Event args for the `DiagnosticReceived` event on /// `ICoreWebView2DiagnosticMonitor`. Each instance /// represents a single diagnostic signal. [uuid(A1B2C3D4-E5F6-7890-ABCD-EF1234567890), object, pointer_default(unique)] -interface ICoreWebView2DiagnosticEventArgs : IUnknown { +interface ICoreWebView2DiagnosticReceivedEventArgs : IUnknown { /// The diagnostic category that this event belongs to. [propget] HRESULT Category( [out, retval] @@ -322,7 +320,7 @@ interface ICoreWebView2DiagnosticEventArgs : IUnknown { /// The scope that originated this diagnostic signal. [propget] HRESULT Scope( [out, retval] - COREWEBVIEW2_DIAGNOSTIC_SOURCE_SCOPE* value); + COREWEBVIEW2_DIAGNOSTIC_SCOPE* value); /// Monotonic timestamp in microseconds since an /// unspecified epoch. You can use this value to order @@ -331,11 +329,7 @@ interface ICoreWebView2DiagnosticEventArgs : IUnknown { [out, retval] INT64* value); /// Returns category-specific diagnostic data as a JSON - /// string for the specified category. - /// - /// The `category` parameter must match the value - /// returned by `get_Category`. If a different category - /// is passed, the method returns `"{}"`. + /// string. /// /// For `COREWEBVIEW2_DIAGNOSTIC_CATEGORY_NETWORK_ERROR` /// the JSON schema is: @@ -363,8 +357,7 @@ interface ICoreWebView2DiagnosticEventArgs : IUnknown { /// this method returns `"{}"`. /// /// Free the returned string with `CoTaskMemFree`. - HRESULT GetCategoryDetailsAsJson( - [in] COREWEBVIEW2_DIAGNOSTIC_CATEGORY category, + HRESULT GetDetailsAsJson( [out, retval] LPWSTR* value); } @@ -377,7 +370,7 @@ interface ICoreWebView2DiagnosticReceivedEventHandler /// Provides the event args for the corresponding event. HRESULT Invoke( [in] ICoreWebView2DiagnosticMonitor* sender, - [in] ICoreWebView2DiagnosticEventArgs* args); + [in] ICoreWebView2DiagnosticReceivedEventArgs* args); } /// A diagnostic monitor that receives diagnostic signals @@ -392,11 +385,17 @@ interface ICoreWebView2DiagnosticReceivedEventHandler /// The monitor is active from creation until it is /// released. Releasing the monitor automatically stops all /// events and clears all filters. +/// +/// All members of this interface must be called on the +/// same thread that created the +/// `ICoreWebView2Environment`. Calling from a different +/// thread returns `RPC_E_WRONG_THREAD`. Handlers must +/// not block this thread. [uuid(E4F5A6B7-C8D9-0123-ABCD-456789012345), object, pointer_default(unique)] interface ICoreWebView2DiagnosticMonitor : IUnknown { - /// Adds a diagnostic filter for the specified category. + /// Sets a diagnostic filter for the specified category. /// After this call, `DiagnosticReceived` will fire for /// events in this category that match the JSON criteria. /// @@ -433,7 +432,7 @@ interface ICoreWebView2DiagnosticMonitor : IUnknown { /// /// Returns `E_INVALIDARG` if the JSON is malformed. /// On failure, the filter state is unchanged. - HRESULT AddDiagnosticReceivedFilter( + HRESULT SetDiagnosticFilter( [in] COREWEBVIEW2_DIAGNOSTIC_CATEGORY category, [in] LPCWSTR jsonFilter); @@ -441,16 +440,16 @@ interface ICoreWebView2DiagnosticMonitor : IUnknown { /// category. After this call, `DiagnosticReceived` /// will no longer fire for events in this category. /// - /// If no filter was previously added for the category, + /// If no filter was previously set for the category, /// this method is a no-op and returns `S_OK`. - HRESULT RemoveDiagnosticReceivedFilter( + HRESULT RemoveDiagnosticFilter( [in] COREWEBVIEW2_DIAGNOSTIC_CATEGORY category); /// Subscribes to diagnostic events on this monitor. /// The handler is invoked on the thread that created /// the environment. It fires every time a diagnostic - /// signal passes a filter added with - /// `AddDiagnosticReceivedFilter`. + /// signal passes a filter set with + /// `SetDiagnosticFilter`. /// /// Multiple handlers can be registered. They are /// invoked in registration order. @@ -479,8 +478,8 @@ interface ICoreWebView2Environment17 /// panel to operate without interfering with each other. /// /// The monitor is active immediately, but no events fire - /// until a filter is added via - /// `AddDiagnosticReceivedFilter`. + /// until a filter is set via + /// `SetDiagnosticFilter`. /// /// Release the monitor to stop receiving events and /// free resources. @@ -505,7 +504,7 @@ namespace Microsoft.Web.WebView2.Core /// Specifies the scope that originated a diagnostic /// event. - enum CoreWebView2DiagnosticSourceScope + enum CoreWebView2DiagnosticScope { /// Signal from a specific WebView instance. WebView = 0, @@ -518,17 +517,16 @@ namespace Microsoft.Web.WebView2.Core }; /// Event args for the DiagnosticReceived event. - runtimeclass CoreWebView2DiagnosticEventArgs + runtimeclass CoreWebView2DiagnosticReceivedEventArgs { CoreWebView2DiagnosticCategory Category { get; }; - CoreWebView2DiagnosticSourceScope Scope { get; }; + CoreWebView2DiagnosticScope Scope { get; }; Int64 Timestamp { get; }; /// Returns category-specific data as a JSON /// string. Returns "{}" for unrecognized /// categories. - String GetCategoryDetailsAsJson( - CoreWebView2DiagnosticCategory category); + String GetDetailsAsJson(); } /// A diagnostic monitor that receives signals from @@ -537,16 +535,16 @@ namespace Microsoft.Web.WebView2.Core runtimeclass CoreWebView2DiagnosticMonitor : Windows.Foundation.IClosable { - void AddDiagnosticReceivedFilter( + void SetDiagnosticFilter( CoreWebView2DiagnosticCategory category, String jsonFilter); - void RemoveDiagnosticReceivedFilter( + void RemoveDiagnosticFilter( CoreWebView2DiagnosticCategory category); event Windows.Foundation.TypedEventHandler< CoreWebView2DiagnosticMonitor, - CoreWebView2DiagnosticEventArgs> + CoreWebView2DiagnosticReceivedEventArgs> DiagnosticReceived; } From 82a64399c58d2a12e26bd2cf82f15da3aea63e86 Mon Sep 17 00:00:00 2001 From: Chetan Pandey Date: Thu, 14 May 2026 15:31:57 +0530 Subject: [PATCH 04/12] Making description more clear --- specs/DiagnosticMonitor.md | 47 ++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/specs/DiagnosticMonitor.md b/specs/DiagnosticMonitor.md index ab7296dfa..9740e5cc5 100644 --- a/specs/DiagnosticMonitor.md +++ b/specs/DiagnosticMonitor.md @@ -11,20 +11,35 @@ interactive (they expect a response), and each has its own event shape. The Diagnostic Monitor API introduces an observation-only monitor -object that delivers diagnostic signals from all layers — WebView, +API that delivers diagnostic signals from all layers — WebView, Profile, and Environment — through a single `DiagnosticReceived` -event. Host apps create a monitor from the environment and opt in +event. Host apps create monitors from the environment and opt in per category using `SetDiagnosticFilter`. # Description You create an `ICoreWebView2DiagnosticMonitor` from the environment -using `CreateDiagnosticMonitor`. The monitor observes diagnostic -signals across all WebViews, profiles, and the environment itself. -You control which categories of events are delivered by calling -`SetDiagnosticFilter` with a category and an optional JSON -filter string. +using `CreateDiagnosticMonitor`. The monitor is strictly +**observation-only** — it reports diagnostic signals but does not +allow the host to intercept, modify, or respond to them. Unlike +interactive APIs such as `ServerCertificateErrorDetected`, the +monitor never expects a response and has no deferral mechanism. +It is designed purely for logging and telemetry, not for making +runtime decisions. + +The monitor observes diagnostic signals across all WebViews, +profiles, and the environment itself. You control which categories +of events are delivered by calling `SetDiagnosticFilter` with a +category and a JSON filter string. + +The API is **JSON-in / JSON-out by design**. Filters are expressed +as JSON objects whose schema is defined and maintained as part of +the API contract. Diagnostic details are returned as JSON strings +whose schema is documented per category with a set of guaranteed +fields; the runtime may include additional key-value pairs beyond +the documented set, so consumers should ignore unknown keys and +must not treat the documented schema as exhaustive. Key scenarios: @@ -344,7 +359,9 @@ interface ICoreWebView2DiagnosticReceivedEventArgs : IUnknown { /// } /// ``` /// - /// `errorCode` is the Chromium net error code (integer). + /// `errorCode` is the Chromium net error code (negative + /// integer). See the list at + /// [net_error_list.h](https://source.chromium.org/chromium/chromium/src/+/main:net/base/net_error_list.h). /// `statusCode` is the HTTP response status code /// (integer, 0 if no response was received). /// `httpMethod` is the HTTP method string. @@ -353,6 +370,11 @@ interface ICoreWebView2DiagnosticReceivedEventArgs : IUnknown { /// `protocol` is the protocol scheme (e.g. "https"). /// `uri` is the request URI. /// + /// The runtime may include additional key-value pairs + /// beyond the fields listed above. Consumers should + /// ignore unknown keys and must not treat the documented + /// schema as exhaustive. + /// /// For categories that the runtime does not yet populate, /// this method returns `"{}"`. /// @@ -399,6 +421,9 @@ interface ICoreWebView2DiagnosticMonitor : IUnknown { /// After this call, `DiagnosticReceived` will fire for /// events in this category that match the JSON criteria. /// + /// The filter JSON schema is defined and maintained as + /// part of the API contract. + /// /// Pass `"{}"` or an empty string as `jsonFilter` to /// receive all events in the category without /// field-level filtering. @@ -524,8 +549,10 @@ namespace Microsoft.Web.WebView2.Core Int64 Timestamp { get; }; /// Returns category-specific data as a JSON - /// string. Returns "{}" for unrecognized - /// categories. + /// string. The runtime may include additional + /// key-value pairs beyond the documented fields; + /// consumers should ignore unknown keys. + /// Returns "{}" for unrecognized categories. String GetDetailsAsJson(); } From dbb37216564a13614afc1ee144f3f1580c61f052 Mon Sep 17 00:00:00 2001 From: Chetan Pandey Date: Fri, 12 Jun 2026 12:59:57 +0530 Subject: [PATCH 05/12] Address review feedback on DiagnosticMonitor spec - Rename COREWEBVIEW2_DIAGNOSTIC_SCOPE_WEB_VIEW to ..._WEBVIEW to match existing one-word convention (e.g. COREWEBVIEW2_*). - Rename JSON field "protocol" to "scheme" and update prose to "URI scheme". - Strengthen errorCode documentation: mark net_error_list.h as the authoritative source, note stability and additive evolution, and instruct consumers to treat unknown codes as generic failures. - Convert GetDetailsAsJson() to a DetailsAsJson property (COM [propget] and WinRT { get; }); update C++ and .NET samples accordingly. - Drop the "returns {} for unrecognized categories" sentence; only one populated category exists. - Add HRESULT Close() to ICoreWebView2DiagnosticMonitor, mirroring the ICoreWebView2SharedBuffer pattern for deterministic teardown. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- specs/DiagnosticMonitor.md | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/specs/DiagnosticMonitor.md b/specs/DiagnosticMonitor.md index 9740e5cc5..e888d9433 100644 --- a/specs/DiagnosticMonitor.md +++ b/specs/DiagnosticMonitor.md @@ -143,7 +143,7 @@ void DiagnosticComponent::HandleDiagnosticEvent( CHECK_FAILURE(args->get_Scope(&scope)); wil::unique_cotaskmem_string detailsJson; - CHECK_FAILURE(args->GetDetailsAsJson(&detailsJson)); + CHECK_FAILURE(args->get_DetailsAsJson(&detailsJson)); INT64 timestamp = 0; CHECK_FAILURE(args->get_Timestamp(×tamp)); @@ -197,7 +197,7 @@ public class DiagnosticComponent : IDisposable args.Scope; long timestamp = args.Timestamp; string detailsJson = - args.GetDetailsAsJson(); + args.DetailsAsJson; Debug.WriteLine( $"[Diagnostic] category={category} " + @@ -309,7 +309,7 @@ typedef enum COREWEBVIEW2_DIAGNOSTIC_CATEGORY { typedef enum COREWEBVIEW2_DIAGNOSTIC_SCOPE { /// The diagnostic signal originated from a specific /// WebView instance. - COREWEBVIEW2_DIAGNOSTIC_SCOPE_WEB_VIEW, + COREWEBVIEW2_DIAGNOSTIC_SCOPE_WEBVIEW, /// The diagnostic signal originated from a profile /// but is not tied to a specific WebView. @@ -354,20 +354,24 @@ interface ICoreWebView2DiagnosticReceivedEventArgs : IUnknown { /// "statusCode": 404, /// "httpMethod": "GET", /// "elapsedTime": 1234, - /// "protocol": "https", + /// "scheme": "https", /// "uri": "https://www.contoso.com/api/data" /// } /// ``` /// - /// `errorCode` is the Chromium net error code (negative - /// integer). See the list at + /// `errorCode` is the Chromium net error code (a negative + /// integer). The complete, authoritative list of values and + /// their meanings is defined in /// [net_error_list.h](https://source.chromium.org/chromium/chromium/src/+/main:net/base/net_error_list.h). + /// Values are stable across releases; new error codes may + /// be added over time, so consumers should treat unknown + /// codes as generic failures. /// `statusCode` is the HTTP response status code /// (integer, 0 if no response was received). /// `httpMethod` is the HTTP method string. /// `elapsedTime` is the request duration in /// milliseconds (integer). - /// `protocol` is the protocol scheme (e.g. "https"). + /// `scheme` is the URI scheme (e.g. "https"). /// `uri` is the request URI. /// /// The runtime may include additional key-value pairs @@ -375,11 +379,8 @@ interface ICoreWebView2DiagnosticReceivedEventArgs : IUnknown { /// ignore unknown keys and must not treat the documented /// schema as exhaustive. /// - /// For categories that the runtime does not yet populate, - /// this method returns `"{}"`. - /// /// Free the returned string with `CoTaskMemFree`. - HRESULT GetDetailsAsJson( + [propget] HRESULT DetailsAsJson( [out, retval] LPWSTR* value); } @@ -487,6 +488,14 @@ interface ICoreWebView2DiagnosticMonitor : IUnknown { /// `add_DiagnosticReceived`. HRESULT remove_DiagnosticReceived( [in] EventRegistrationToken token); + + /// Release the diagnostic subscription and any registered + /// handlers. The application should call this API when no + /// access to the monitor is needed any more, to ensure + /// that the underlying resources are released timely even + /// if the monitor object itself is not released due to + /// some leaked reference. + HRESULT Close(); } interface ICoreWebView2Environment17 @@ -552,8 +561,7 @@ namespace Microsoft.Web.WebView2.Core /// string. The runtime may include additional /// key-value pairs beyond the documented fields; /// consumers should ignore unknown keys. - /// Returns "{}" for unrecognized categories. - String GetDetailsAsJson(); + String DetailsAsJson { get; }; } /// A diagnostic monitor that receives signals from From 3b83f093d638fba7ca8a6dad7e06dc0c1495e975 Mon Sep 17 00:00:00 2001 From: Chetan Pandey Date: Fri, 12 Jun 2026 13:28:16 +0530 Subject: [PATCH 06/12] Address remaining review feedback: NETWORK_REQUEST rename, wall-clock Timestamp, drop profileName - Rename COREWEBVIEW2_DIAGNOSTIC_CATEGORY_NETWORK_ERROR to ..._NETWORK_REQUEST (WinRT: NetworkError -> NetworkRequest). The category emits a per-request lifecycle signal, not failures only; errorCode == 0 means success. - Rewrite the category description to spell out exactly which requests fire it (navigation, sub-resource, fetch/XHR, dedicated/shared workers, attached service workers) and what is excluded (other host apps sharing the user data folder, CSP violation reports). - Switch Timestamp to wall-clock: COM double seconds since UNIX epoch, WinRT Windows.Foundation.DateTime. Enables correlation with other timestamped telemetry sources. - Drop profileName from the filter schema, both code samples, and intro prose to keep the filter consistent with the emitted Details. - Update intro and example prose to refer to "network requests" rather than "network errors". Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- specs/DiagnosticMonitor.md | 113 +++++++++++++++++++++---------------- 1 file changed, 64 insertions(+), 49 deletions(-) diff --git a/specs/DiagnosticMonitor.md b/specs/DiagnosticMonitor.md index e888d9433..413fd60e3 100644 --- a/specs/DiagnosticMonitor.md +++ b/specs/DiagnosticMonitor.md @@ -4,11 +4,11 @@ Diagnostic Monitor API # Background WebView2 host applications today lack a unified way to observe -diagnostic signals — such as network failures — across all WebView -instances and profiles within an environment. Existing APIs such as -`ServerCertificateErrorDetected` are per-WebView or per-profile, -interactive (they expect a response), and each has its own event -shape. +diagnostic signals — such as network request completions — across +all WebView instances and profiles within an environment. Existing +APIs such as `ServerCertificateErrorDetected` are per-WebView or +per-profile, interactive (they expect a response), and each has +its own event shape. The Diagnostic Monitor API introduces an observation-only monitor API that delivers diagnostic signals from all layers — WebView, @@ -43,10 +43,10 @@ must not treat the documented schema as exhaustive. Key scenarios: -* **Telemetry** — subscribe to all network errors and forward the +* **Telemetry** — subscribe to all network requests and forward the JSON details to your telemetry backend. -* **Targeted monitoring** — filter to specific error codes, HTTP - methods, or profile names using the JSON filter. +* **Targeted monitoring** — filter to specific error codes or HTTP + methods using the JSON filter. * **Multiple consumers** — create separate monitors for telemetry and a debug panel, each with independent filters. @@ -61,7 +61,7 @@ the monitor stops all events and clears all filters automatically. ### Win32 C++ The following example creates a `DiagnosticMonitor`, adds a filter -for network errors, and subscribes to the `DiagnosticReceived` +for network requests, and subscribes to the `DiagnosticReceived` event. ```cpp @@ -110,11 +110,12 @@ void DiagnosticComponent::SetupDiagnostics() CHECK_FAILURE( m_environment->CreateDiagnosticMonitor(&m_monitor)); - // Add a filter for NETWORK_ERROR. Pass "{}" to receive - // all network errors without field-level filtering. + // Add a filter for NETWORK_REQUEST. Pass "{}" to + // receive all network requests without field-level + // filtering. CHECK_FAILURE( m_monitor->SetDiagnosticFilter( - COREWEBVIEW2_DIAGNOSTIC_CATEGORY_NETWORK_ERROR, + COREWEBVIEW2_DIAGNOSTIC_CATEGORY_NETWORK_REQUEST, L"{}")); // Subscribe to the diagnostic event. @@ -175,11 +176,11 @@ public class DiagnosticComponent : IDisposable // Create a diagnostic monitor. _monitor = environment.CreateDiagnosticMonitor(); - // Add a filter for NetworkError. Pass "{}" to - // receive all network errors without field-level - // filtering. + // Add a filter for NetworkRequest. Pass "{}" to + // receive all network requests without + // field-level filtering. _monitor.SetDiagnosticFilter( - CoreWebView2DiagnosticCategory.NetworkError, + CoreWebView2DiagnosticCategory.NetworkRequest, "{}"); // Subscribe to the diagnostic event. @@ -237,12 +238,11 @@ void DiagnosticComponent::SetupFilteredDiagnostics() // Only receive DNS (ERR_NAME_NOT_RESOLVED, -105) // and timeout (ERR_TIMED_OUT, -7) errors - // for GET/POST requests from the "Default" profile. + // for GET/POST requests. CHECK_FAILURE( m_monitor->SetDiagnosticFilter( - COREWEBVIEW2_DIAGNOSTIC_CATEGORY_NETWORK_ERROR, + COREWEBVIEW2_DIAGNOSTIC_CATEGORY_NETWORK_REQUEST, LR"({ - "profileName": "Default", "errorCode": [-105, -7], "httpMethod": ["GET", "POST"] })")); @@ -273,12 +273,10 @@ private void SetupFilteredDiagnostics() _monitor = _environment.CreateDiagnosticMonitor(); // Only DNS (ERR_NAME_NOT_RESOLVED, -105) and timeout - // (ERR_TIMED_OUT, -7) errors for GET/POST requests - // from the "Default" profile. + // (ERR_TIMED_OUT, -7) errors for GET/POST requests. _monitor.SetDiagnosticFilter( - CoreWebView2DiagnosticCategory.NetworkError, + CoreWebView2DiagnosticCategory.NetworkRequest, @"{ - ""profileName"": ""Default"", ""errorCode"": [-105, -7], ""httpMethod"": [""GET"", ""POST""] }"); @@ -297,11 +295,21 @@ private void SetupFilteredDiagnostics() /// Specifies the category of diagnostic event. [v1_enum] typedef enum COREWEBVIEW2_DIAGNOSTIC_CATEGORY { - /// Network request failure including DNS resolution - /// errors, TLS handshake failures, connection timeouts, - /// HTTP error status codes (4xx/5xx), CORS violations, - /// and mixed-content blocked requests. - COREWEBVIEW2_DIAGNOSTIC_CATEGORY_NETWORK_ERROR, + /// Network request lifecycle signal. Fires once per + /// network request issued by any `CoreWebView2` created + /// from this environment, after the request completes — + /// including top-level navigations, sub-resource loads, + /// `fetch`/`XHR`, dedicated and shared worker requests, + /// and service-worker requests associated with one of + /// those WebViews. Does not include requests from other + /// host applications sharing the same user data folder, + /// nor CSP violation reports. + /// + /// The `errorCode` field on the details indicates the + /// outcome: `0` for success, non-zero for failure (see + /// `errorCode` in the details schema below for the + /// authoritative reference). + COREWEBVIEW2_DIAGNOSTIC_CATEGORY_NETWORK_REQUEST, } COREWEBVIEW2_DIAGNOSTIC_CATEGORY; /// Specifies the scope that originated a diagnostic event. @@ -337,16 +345,20 @@ interface ICoreWebView2DiagnosticReceivedEventArgs : IUnknown { [out, retval] COREWEBVIEW2_DIAGNOSTIC_SCOPE* value); - /// Monotonic timestamp in microseconds since an - /// unspecified epoch. You can use this value to order - /// events but should not convert it to wall-clock time. + /// The wall-clock time at which the runtime observed this + /// diagnostic event, as the number of seconds since the + /// UNIX epoch (1970-01-01T00:00:00Z, UTC). Use this value + /// to correlate diagnostic events with other timestamped + /// telemetry. The value is derived from the system clock + /// and may be affected by clock adjustments (for example, + /// NTP). [propget] HRESULT Timestamp( - [out, retval] INT64* value); + [out, retval] double* value); /// Returns category-specific diagnostic data as a JSON /// string. /// - /// For `COREWEBVIEW2_DIAGNOSTIC_CATEGORY_NETWORK_ERROR` + /// For `COREWEBVIEW2_DIAGNOSTIC_CATEGORY_NETWORK_REQUEST` /// the JSON schema is: /// ``` /// { @@ -430,14 +442,12 @@ interface ICoreWebView2DiagnosticMonitor : IUnknown { /// field-level filtering. /// /// Pass a JSON object to apply field-level filtering. - /// The object's keys are detail field names. - /// `profileName` is a single string value; all other - /// fields are arrays of accepted values. + /// The object's keys are detail field names, each + /// mapped to an array of accepted values. /// - /// Example for `NETWORK_ERROR`: + /// Example for `NETWORK_REQUEST`: /// ``` /// { - /// "profileName": "Default", /// "errorCode": [-105, -7], /// "statusCode": [404, 500], /// "uriPattern": ["https://*.contoso.com/*"], @@ -445,13 +455,10 @@ interface ICoreWebView2DiagnosticMonitor : IUnknown { /// } /// ``` /// - /// `profileName` is a single string that must match - /// the profile name exactly. All other fields are - /// arrays of accepted values. An event passes if it - /// matches any value in each specified field - /// (OR within a field, AND across fields). String - /// fields in `uriPattern` support wildcard patterns - /// using `*` and `?`. + /// An event passes if it matches any value in each + /// specified field (OR within a field, AND across + /// fields). String fields in `uriPattern` support + /// wildcard patterns using `*` and `?`. /// /// Calling this method again for the same category /// replaces the previous filter for that category. @@ -531,9 +538,17 @@ namespace Microsoft.Web.WebView2.Core /// Specifies the category of diagnostic event. enum CoreWebView2DiagnosticCategory { - /// Network request failure (DNS, TLS, timeout, - /// HTTP error, CORS, mixed content). - NetworkError = 0, + /// Network request lifecycle signal. Fires once + /// per network request issued by any CoreWebView2 + /// created from this environment, after the request + /// completes — including top-level navigations, + /// sub-resource loads, fetch/XHR, dedicated and + /// shared worker requests, and service-worker + /// requests associated with one of those WebViews. + /// Does not include requests from other host + /// applications sharing the same user data folder, + /// nor CSP violation reports. + NetworkRequest = 0, }; /// Specifies the scope that originated a diagnostic @@ -555,7 +570,7 @@ namespace Microsoft.Web.WebView2.Core { CoreWebView2DiagnosticCategory Category { get; }; CoreWebView2DiagnosticScope Scope { get; }; - Int64 Timestamp { get; }; + Windows.Foundation.DateTime Timestamp { get; }; /// Returns category-specific data as a JSON /// string. The runtime may include additional From 3fd6150d623efe94feb2825c47b17ac2f50a9819 Mon Sep 17 00:00:00 2001 From: Chetan Pandey Date: Fri, 12 Jun 2026 14:35:12 +0530 Subject: [PATCH 07/12] Move per-category JSON schemas onto the enum values The filter JSON schema and the DetailsAsJson schema vary per category, so they belong on each COREWEBVIEW2_DIAGNOSTIC_CATEGORY value rather than on the category-agnostic methods. As we add more categories the method-level docs would otherwise grow into a per-category dumping ground. - Move NETWORK_REQUEST filter schema (errorCode/statusCode/uriPattern/ httpMethod, wildcard rules, AND/OR semantics) and Details schema (errorCode/statusCode/httpMethod/elapsedTime/scheme/uri, with field descriptions) onto the COREWEBVIEW2_DIAGNOSTIC_CATEGORY_NETWORK_REQUEST enum value. Update the top-of-enum doc comment to point readers there. - Strip SetDiagnosticFilter down to category-agnostic behavior (empty filter = match all, replace-on-recall, E_INVALIDARG on bad JSON or schema mismatch) and link to the enum for the schema. - Strip DetailsAsJson down to category-agnostic behavior and link to the enum for the schema. - Mirror the same restructure on the WinRT enum and DetailsAsJson property. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- specs/DiagnosticMonitor.md | 174 +++++++++++++++++++++++-------------- 1 file changed, 110 insertions(+), 64 deletions(-) diff --git a/specs/DiagnosticMonitor.md b/specs/DiagnosticMonitor.md index 413fd60e3..830e790ee 100644 --- a/specs/DiagnosticMonitor.md +++ b/specs/DiagnosticMonitor.md @@ -292,7 +292,11 @@ private void SetupFilteredDiagnostics() ## Win32 C++ ```idl -/// Specifies the category of diagnostic event. +/// Specifies the category of diagnostic event. Each value +/// defines its own JSON schemas for the filter accepted by +/// `ICoreWebView2DiagnosticMonitor::SetDiagnosticFilter` +/// and for the details returned by +/// `ICoreWebView2DiagnosticReceivedEventArgs::DetailsAsJson`. [v1_enum] typedef enum COREWEBVIEW2_DIAGNOSTIC_CATEGORY { /// Network request lifecycle signal. Fires once per @@ -305,10 +309,48 @@ typedef enum COREWEBVIEW2_DIAGNOSTIC_CATEGORY { /// host applications sharing the same user data folder, /// nor CSP violation reports. /// - /// The `errorCode` field on the details indicates the - /// outcome: `0` for success, non-zero for failure (see - /// `errorCode` in the details schema below for the - /// authoritative reference). + /// **Filter schema** (accepted by `SetDiagnosticFilter`): + /// Each key maps to an array of accepted values. An event + /// passes if it matches any value in each specified field + /// (OR within a field, AND across fields). `uriPattern` + /// supports `*` and `?` wildcards. + /// ``` + /// { + /// "errorCode": [-105, -7], + /// "statusCode": [404, 500], + /// "uriPattern": ["https://*.contoso.com/*"], + /// "httpMethod": ["GET", "POST"] + /// } + /// ``` + /// + /// **Details schema** (returned by `DetailsAsJson`): + /// ``` + /// { + /// "errorCode": -105, + /// "statusCode": 404, + /// "httpMethod": "GET", + /// "elapsedTime": 1234, + /// "scheme": "https", + /// "uri": "https://www.contoso.com/api/data" + /// } + /// ``` + /// `errorCode` is the Chromium net error code (`0` = + /// success, non-zero = failure). The complete, + /// authoritative list of values is defined in + /// [net_error_list.h](https://source.chromium.org/chromium/chromium/src/+/main:net/base/net_error_list.h). + /// Values are stable across releases; new error codes may + /// be added over time, so consumers should treat unknown + /// codes as generic failures. + /// `statusCode` is the HTTP response status code (0 if + /// no response was received). + /// `httpMethod` is the HTTP method string. + /// `elapsedTime` is the request duration in milliseconds. + /// `scheme` is the URI scheme (for example, "https"). + /// `uri` is the request URI. + /// + /// The runtime may include additional key-value pairs + /// beyond those listed above. Consumers should ignore + /// unknown keys. COREWEBVIEW2_DIAGNOSTIC_CATEGORY_NETWORK_REQUEST, } COREWEBVIEW2_DIAGNOSTIC_CATEGORY; @@ -356,40 +398,13 @@ interface ICoreWebView2DiagnosticReceivedEventArgs : IUnknown { [out, retval] double* value); /// Returns category-specific diagnostic data as a JSON - /// string. - /// - /// For `COREWEBVIEW2_DIAGNOSTIC_CATEGORY_NETWORK_REQUEST` - /// the JSON schema is: - /// ``` - /// { - /// "errorCode": -105, - /// "statusCode": 404, - /// "httpMethod": "GET", - /// "elapsedTime": 1234, - /// "scheme": "https", - /// "uri": "https://www.contoso.com/api/data" - /// } - /// ``` - /// - /// `errorCode` is the Chromium net error code (a negative - /// integer). The complete, authoritative list of values and - /// their meanings is defined in - /// [net_error_list.h](https://source.chromium.org/chromium/chromium/src/+/main:net/base/net_error_list.h). - /// Values are stable across releases; new error codes may - /// be added over time, so consumers should treat unknown - /// codes as generic failures. - /// `statusCode` is the HTTP response status code - /// (integer, 0 if no response was received). - /// `httpMethod` is the HTTP method string. - /// `elapsedTime` is the request duration in - /// milliseconds (integer). - /// `scheme` is the URI scheme (e.g. "https"). - /// `uri` is the request URI. + /// string. The schema for each category is documented on + /// the corresponding `COREWEBVIEW2_DIAGNOSTIC_CATEGORY` + /// enum value. /// /// The runtime may include additional key-value pairs - /// beyond the fields listed above. Consumers should - /// ignore unknown keys and must not treat the documented - /// schema as exhaustive. + /// beyond the documented fields. Consumers should ignore + /// unknown keys. /// /// Free the returned string with `CoTaskMemFree`. [propget] HRESULT DetailsAsJson( @@ -434,37 +449,20 @@ interface ICoreWebView2DiagnosticMonitor : IUnknown { /// After this call, `DiagnosticReceived` will fire for /// events in this category that match the JSON criteria. /// - /// The filter JSON schema is defined and maintained as - /// part of the API contract. + /// The filter JSON schema is category-specific and is + /// documented on the corresponding + /// `COREWEBVIEW2_DIAGNOSTIC_CATEGORY` enum value. /// /// Pass `"{}"` or an empty string as `jsonFilter` to /// receive all events in the category without /// field-level filtering. /// - /// Pass a JSON object to apply field-level filtering. - /// The object's keys are detail field names, each - /// mapped to an array of accepted values. - /// - /// Example for `NETWORK_REQUEST`: - /// ``` - /// { - /// "errorCode": [-105, -7], - /// "statusCode": [404, 500], - /// "uriPattern": ["https://*.contoso.com/*"], - /// "httpMethod": ["GET", "POST"] - /// } - /// ``` - /// - /// An event passes if it matches any value in each - /// specified field (OR within a field, AND across - /// fields). String fields in `uriPattern` support - /// wildcard patterns using `*` and `?`. - /// /// Calling this method again for the same category /// replaces the previous filter for that category. /// - /// Returns `E_INVALIDARG` if the JSON is malformed. - /// On failure, the filter state is unchanged. + /// Returns `E_INVALIDARG` if the JSON is malformed or + /// does not match the category's filter schema. On + /// failure, the filter state is unchanged. HRESULT SetDiagnosticFilter( [in] COREWEBVIEW2_DIAGNOSTIC_CATEGORY category, [in] LPCWSTR jsonFilter); @@ -535,7 +533,10 @@ interface ICoreWebView2Environment17 ```c# namespace Microsoft.Web.WebView2.Core { - /// Specifies the category of diagnostic event. + /// Specifies the category of diagnostic event. Each + /// value defines its own JSON schemas for the filter + /// accepted by SetDiagnosticFilter and for the details + /// returned by DetailsAsJson. enum CoreWebView2DiagnosticCategory { /// Network request lifecycle signal. Fires once @@ -548,6 +549,48 @@ namespace Microsoft.Web.WebView2.Core /// Does not include requests from other host /// applications sharing the same user data folder, /// nor CSP violation reports. + /// + /// Filter schema (accepted by SetDiagnosticFilter). + /// Each key maps to an array of accepted values. + /// An event passes if it matches any value in each + /// specified field (OR within a field, AND across + /// fields). `uriPattern` supports `*` and `?` + /// wildcards. + /// { + /// "errorCode": [-105, -7], + /// "statusCode": [404, 500], + /// "uriPattern": ["https://*.contoso.com/*"], + /// "httpMethod": ["GET", "POST"] + /// } + /// + /// Details schema (returned by DetailsAsJson). + /// { + /// "errorCode": -105, + /// "statusCode": 404, + /// "httpMethod": "GET", + /// "elapsedTime": 1234, + /// "scheme": "https", + /// "uri": "https://www.contoso.com/api/data" + /// } + /// `errorCode` is the Chromium net error code + /// (0 = success, non-zero = failure). The complete, + /// authoritative list of values is defined in + /// net_error_list.h. Values are stable across + /// releases; new error codes may be added over + /// time, so consumers should treat unknown codes + /// as generic failures. + /// `statusCode` is the HTTP response status code + /// (0 if no response was received). + /// `httpMethod` is the HTTP method string. + /// `elapsedTime` is the request duration in + /// milliseconds. + /// `scheme` is the URI scheme (for example, + /// "https"). + /// `uri` is the request URI. + /// + /// The runtime may include additional key-value + /// pairs beyond those listed above. Consumers + /// should ignore unknown keys. NetworkRequest = 0, }; @@ -573,9 +616,12 @@ namespace Microsoft.Web.WebView2.Core Windows.Foundation.DateTime Timestamp { get; }; /// Returns category-specific data as a JSON - /// string. The runtime may include additional - /// key-value pairs beyond the documented fields; - /// consumers should ignore unknown keys. + /// string. The schema for each category is + /// documented on the corresponding + /// CoreWebView2DiagnosticCategory enum value. The + /// runtime may include additional key-value pairs + /// beyond the documented fields; consumers should + /// ignore unknown keys. String DetailsAsJson { get; }; } From fd50886710d673509375c963689892bf2bae347f Mon Sep 17 00:00:00 2001 From: Chetan Pandey Date: Fri, 12 Jun 2026 15:19:51 +0530 Subject: [PATCH 08/12] Reframe Background and Description around field diagnostics Rewrite the Background and Description sections to position the Diagnostic Monitor API as an on-demand field diagnostics surface for non-functioning deployments, not as a general error-handling or telemetry mechanism. Clarifies that the API is complementary and tangential to existing error-handling APIs and is intended to be turned on externally to capture detailed diagnostic data from a specific misbehaving instance. Also justifies the JSON-in / JSON-out shape as the right contract for shipping new categories and fields to deployments already in the field, and notes that monitors stay inert until SetDiagnosticFilter is called. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- specs/DiagnosticMonitor.md | 92 +++++++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 36 deletions(-) diff --git a/specs/DiagnosticMonitor.md b/specs/DiagnosticMonitor.md index 830e790ee..bbe546897 100644 --- a/specs/DiagnosticMonitor.md +++ b/specs/DiagnosticMonitor.md @@ -3,18 +3,27 @@ Diagnostic Monitor API # Background -WebView2 host applications today lack a unified way to observe -diagnostic signals — such as network request completions — across -all WebView instances and profiles within an environment. Existing -APIs such as `ServerCertificateErrorDetected` are per-WebView or -per-profile, interactive (they expect a response), and each has -its own event shape. - -The Diagnostic Monitor API introduces an observation-only monitor -API that delivers diagnostic signals from all layers — WebView, -Profile, and Environment — through a single `DiagnosticReceived` -event. Host apps create monitors from the environment and opt in -per category using `SetDiagnosticFilter`. +WebView2 host applications get deployed in diverse environments, +and those environments occasionally surface problems that cannot +be recovered from — or even diagnosed — by the normal in-app +error paths. When that happens, the only recourse is detailed +logging information from the affected WebView2 instance, often +behind a security boundary that prevents the host app itself +from collecting it inline. + +None of today's WebView2 APIs cover this scenario. Existing +error-handling APIs such as `ServerCertificateErrorDetected` are +interactive, per-instance, and shaped around in-flow decisions — +not around capturing rich, free-form diagnostic data from a +misbehaving deployment after the fact. + +The Diagnostic Monitor API addresses this gap. It is an +**observation-only** surface that lets a host app opt in, +typically by external trigger, to collect detailed diagnostic +information from a specific non-functioning instance when the +situation demands it. The API is complementary and tangential +to regular error handling and is **not** intended as a general +error-handling mechanism. # Description @@ -22,36 +31,47 @@ per category using `SetDiagnosticFilter`. You create an `ICoreWebView2DiagnosticMonitor` from the environment using `CreateDiagnosticMonitor`. The monitor is strictly **observation-only** — it reports diagnostic signals but does not -allow the host to intercept, modify, or respond to them. Unlike -interactive APIs such as `ServerCertificateErrorDetected`, the +allow the host to intercept, modify, or respond to them. The monitor never expects a response and has no deferral mechanism. -It is designed purely for logging and telemetry, not for making -runtime decisions. +It exists to capture rich diagnostic data on demand for offline +analysis, not to drive runtime decisions. -The monitor observes diagnostic signals across all WebViews, +A monitor observes diagnostic signals across all WebViews, profiles, and the environment itself. You control which categories of events are delivered by calling `SetDiagnosticFilter` with a -category and a JSON filter string. +category and a JSON filter string; nothing is delivered until at +least one filter is set. This keeps the monitor inert until a +host explicitly turns it on — for example, in response to an +external diagnostic trigger targeting a specific deployment. The API is **JSON-in / JSON-out by design**. Filters are expressed -as JSON objects whose schema is defined and maintained as part of -the API contract. Diagnostic details are returned as JSON strings -whose schema is documented per category with a set of guaranteed -fields; the runtime may include additional key-value pairs beyond -the documented set, so consumers should ignore unknown keys and -must not treat the documented schema as exhaustive. - -Key scenarios: - -* **Telemetry** — subscribe to all network requests and forward the - JSON details to your telemetry backend. -* **Targeted monitoring** — filter to specific error codes or HTTP - methods using the JSON filter. -* **Multiple consumers** — create separate monitors for telemetry - and a debug panel, each with independent filters. - -The monitor is active from creation until it is released. Releasing -the monitor stops all events and clears all filters automatically. +as JSON objects whose schema is defined per category and maintained +as part of the API contract. Diagnostic details are returned as +JSON strings whose schema is also documented per category, with a +set of guaranteed fields; the runtime may include additional +key-value pairs beyond the documented set, so consumers should +ignore unknown keys and must not treat the documented schema as +exhaustive. The JSON-in / JSON-out shape is intentional: it lets +new categories and fields ship without breaking the API contract +or requiring host-app updates, which matters for an API whose +primary job is to collect diagnostic data from deployments that +are already in the field. + +Typical scenarios: + +* **Targeted field diagnostics** — turn the monitor on for a + specific non-functioning deployment in response to an external + trigger, capture detailed diagnostic data, and turn it back + off. +* **Telemetry** — subscribe to a category and forward the JSON + details to your telemetry backend. +* **Multiple consumers** — create separate monitors for a + diagnostic capture session and a debug panel, each with + independent filters. + +The monitor is active from creation until it is released or +Closed. Releasing or closing the monitor stops all events and +clears all filters automatically. # Examples From 0b983724e7922026daef154ef293a20c63ff2631 Mon Sep 17 00:00:00 2001 From: Chetan Pandey Date: Fri, 12 Jun 2026 15:31:30 +0530 Subject: [PATCH 09/12] Reword Description section to match repo voice Rewrite the Description in third-person declarative voice matching the convention used in other WebView2Feedback specs (CookieManagement, ClearBrowsingData, CustomDownload, Autofill). Replace "you/we" and phrases like "Its purpose is", "matters for", "shape is intentional" with named subjects ("The monitor", "The host app", "A monitor") and direct statements. Preserves all content and approximate length. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- specs/DiagnosticMonitor.md | 82 +++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/specs/DiagnosticMonitor.md b/specs/DiagnosticMonitor.md index bbe546897..f0405b1df 100644 --- a/specs/DiagnosticMonitor.md +++ b/specs/DiagnosticMonitor.md @@ -28,50 +28,50 @@ error-handling mechanism. # Description -You create an `ICoreWebView2DiagnosticMonitor` from the environment -using `CreateDiagnosticMonitor`. The monitor is strictly -**observation-only** — it reports diagnostic signals but does not -allow the host to intercept, modify, or respond to them. The -monitor never expects a response and has no deferral mechanism. -It exists to capture rich diagnostic data on demand for offline -analysis, not to drive runtime decisions. - -A monitor observes diagnostic signals across all WebViews, -profiles, and the environment itself. You control which categories -of events are delivered by calling `SetDiagnosticFilter` with a -category and a JSON filter string; nothing is delivered until at -least one filter is set. This keeps the monitor inert until a -host explicitly turns it on — for example, in response to an -external diagnostic trigger targeting a specific deployment. - -The API is **JSON-in / JSON-out by design**. Filters are expressed -as JSON objects whose schema is defined per category and maintained -as part of the API contract. Diagnostic details are returned as -JSON strings whose schema is also documented per category, with a -set of guaranteed fields; the runtime may include additional -key-value pairs beyond the documented set, so consumers should -ignore unknown keys and must not treat the documented schema as -exhaustive. The JSON-in / JSON-out shape is intentional: it lets -new categories and fields ship without breaking the API contract -or requiring host-app updates, which matters for an API whose -primary job is to collect diagnostic data from deployments that -are already in the field. +`ICoreWebView2DiagnosticMonitor` is created from the environment +via `ICoreWebView2Environment17::CreateDiagnosticMonitor`. The +monitor is strictly **observation-only**: it reports diagnostic +signals through a single `DiagnosticReceived` event. The host app +cannot intercept, modify, or respond to a signal, and the event +has no deferral mechanism. The API is intended for capturing +diagnostic data on demand for offline analysis, not for driving +runtime decisions. + +A monitor observes diagnostic signals from all WebViews, +profiles, and the environment itself. The host app selects which +categories of events are delivered by calling +`SetDiagnosticFilter` with a category and a JSON filter string. +A newly created monitor delivers no events until at least one +filter is set, so a monitor remains inert until the host app +explicitly turns it on — for example, in response to an external +diagnostic trigger targeting a specific deployment. + +The API is **JSON-in / JSON-out by design**. Filters are JSON +objects whose schema is defined per category. Diagnostic details +are returned as JSON strings whose schema is also defined per +category, with a set of guaranteed fields. The runtime may +include additional key-value pairs beyond the documented set, so +consumers must ignore unknown keys and must not treat the +documented schema as exhaustive. This JSON-in / JSON-out shape +allows new categories and fields to ship without breaking the API +contract or requiring host-app updates, so that the API can +collect diagnostic data from deployments already in the field. Typical scenarios: -* **Targeted field diagnostics** — turn the monitor on for a - specific non-functioning deployment in response to an external - trigger, capture detailed diagnostic data, and turn it back - off. -* **Telemetry** — subscribe to a category and forward the JSON - details to your telemetry backend. -* **Multiple consumers** — create separate monitors for a - diagnostic capture session and a debug panel, each with - independent filters. - -The monitor is active from creation until it is released or -Closed. Releasing or closing the monitor stops all events and -clears all filters automatically. +* **Targeted field diagnostics** — the host app turns the + monitor on for a specific non-functioning deployment in + response to an external trigger, captures detailed diagnostic + data, and turns it back off. +* **Telemetry** — the host app subscribes to a category and + forwards the JSON details to a telemetry backend. +* **Multiple consumers** — separate monitors run independently + for a diagnostic capture session and a debug panel, each with + its own filters. + +A monitor is active from creation until it is released or +closed via `Close`. Either action stops all events and clears +all filters automatically. # Examples From 0d68e15ef3a076dda516c28fc9add0610792a1a1 Mon Sep 17 00:00:00 2001 From: Chetan Pandey Date: Mon, 15 Jun 2026 14:08:40 +0530 Subject: [PATCH 10/12] Address Shrinath's review feedback on Background section - Rephrase 'security boundary ... collecting it inline' to clarify that today there is no built-in way for the host app to collect this additional WebView2 logging data that is not available otherwise. - Reword 'in-flow decisions' to 'real-time decisions'. - Replace 'free-form diagnostic data' with 'varying diagnostic information that can be logged'. - Replace 'observation-only' with 'logging' (both occurrences) and update 'A monitor observes' to 'A monitor captures' for consistency with the new logging framing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- specs/DiagnosticMonitor.md | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/specs/DiagnosticMonitor.md b/specs/DiagnosticMonitor.md index f0405b1df..98b163c53 100644 --- a/specs/DiagnosticMonitor.md +++ b/specs/DiagnosticMonitor.md @@ -7,22 +7,24 @@ WebView2 host applications get deployed in diverse environments, and those environments occasionally surface problems that cannot be recovered from — or even diagnosed — by the normal in-app error paths. When that happens, the only recourse is detailed -logging information from the affected WebView2 instance, often -behind a security boundary that prevents the host app itself -from collecting it inline. +logging information from the affected WebView2 instance — but +today the host app has no built-in way to collect this +additional WebView2 logging data that is not available +otherwise. None of today's WebView2 APIs cover this scenario. Existing error-handling APIs such as `ServerCertificateErrorDetected` are -interactive, per-instance, and shaped around in-flow decisions — -not around capturing rich, free-form diagnostic data from a -misbehaving deployment after the fact. - -The Diagnostic Monitor API addresses this gap. It is an -**observation-only** surface that lets a host app opt in, -typically by external trigger, to collect detailed diagnostic -information from a specific non-functioning instance when the -situation demands it. The API is complementary and tangential -to regular error handling and is **not** intended as a general +interactive, per-instance, and shaped around real-time +decisions — not around capturing rich, varying diagnostic +information that can be logged from a misbehaving deployment +after the fact. + +The Diagnostic Monitor API addresses this gap. It is a +**logging** surface that lets a host app opt in, typically by +external trigger, to collect detailed diagnostic information +from a specific non-functioning instance when the situation +demands it. The API is complementary and tangential to regular +error handling and is **not** intended as a general error-handling mechanism. @@ -30,14 +32,14 @@ error-handling mechanism. `ICoreWebView2DiagnosticMonitor` is created from the environment via `ICoreWebView2Environment17::CreateDiagnosticMonitor`. The -monitor is strictly **observation-only**: it reports diagnostic +monitor is strictly for **logging**: it reports diagnostic signals through a single `DiagnosticReceived` event. The host app cannot intercept, modify, or respond to a signal, and the event has no deferral mechanism. The API is intended for capturing diagnostic data on demand for offline analysis, not for driving runtime decisions. -A monitor observes diagnostic signals from all WebViews, +A monitor captures diagnostic signals from all WebViews, profiles, and the environment itself. The host app selects which categories of events are delivered by calling `SetDiagnosticFilter` with a category and a JSON filter string. From 9e04695644910abc708ff124774d6860d44fdf05 Mon Sep 17 00:00:00 2001 From: shrinaths <40056704+shrinaths@users.noreply.github.com> Date: Mon, 15 Jun 2026 14:53:22 +0530 Subject: [PATCH 11/12] Update DiagnosticMonitor.md --- specs/DiagnosticMonitor.md | 39 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/specs/DiagnosticMonitor.md b/specs/DiagnosticMonitor.md index 98b163c53..c383d54c0 100644 --- a/specs/DiagnosticMonitor.md +++ b/specs/DiagnosticMonitor.md @@ -3,30 +3,21 @@ Diagnostic Monitor API # Background -WebView2 host applications get deployed in diverse environments, -and those environments occasionally surface problems that cannot -be recovered from — or even diagnosed — by the normal in-app -error paths. When that happens, the only recourse is detailed -logging information from the affected WebView2 instance — but -today the host app has no built-in way to collect this -additional WebView2 logging data that is not available -otherwise. - -None of today's WebView2 APIs cover this scenario. Existing -error-handling APIs such as `ServerCertificateErrorDetected` are -interactive, per-instance, and shaped around real-time -decisions — not around capturing rich, varying diagnostic -information that can be logged from a misbehaving deployment -after the fact. - -The Diagnostic Monitor API addresses this gap. It is a -**logging** surface that lets a host app opt in, typically by -external trigger, to collect detailed diagnostic information -from a specific non-functioning instance when the situation -demands it. The API is complementary and tangential to regular -error handling and is **not** intended as a general -error-handling mechanism. - +WebView2 host applications are deployed in diverse environments, +which can occasionally expose issues that cannot be recovered +from—or even diagnosed—through normal in-app error paths. In +addition, certain internal code paths do not expose errors through +the public API by design. When these issues accumulate and lead to +failure, the only recourse is access to detailed diagnostic information +from the affected WebView2 instance. However, host applications +currently have no way to collect this additional logging data. + +The Diagnostic Monitor API addresses this gap. It provides a **logging** +surface that allows a host application to opt in—typically via an external +trigger such as a registry key or environment setting—to collect detailed +diagnostic information from a specific instance when needed. This API is +complementary to standard error handling and is **not** intended to serve as +a general-purpose error-handling mechanism. # Description From f65ec03383406326c1f9cca15e3a120df13f6cff Mon Sep 17 00:00:00 2001 From: Chetan Pandey Date: Mon, 15 Jun 2026 15:07:05 +0530 Subject: [PATCH 12/12] Switch Timestamp to milliseconds since UNIX epoch (INT64) - COM IDL: change Timestamp from double seconds to INT64 milliseconds since the UNIX epoch. Aligns with the INT64 timestamp variable already used in the Win32 C++ sample. - WinRT: change Timestamp from Windows.Foundation.DateTime to Int64 so the projection mirrors the COM type and matches the long timestamp variable already used in the .NET sample. - Update the doc comment to say 'milliseconds since the UNIX epoch' instead of 'seconds since the UNIX epoch'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- specs/DiagnosticMonitor.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/specs/DiagnosticMonitor.md b/specs/DiagnosticMonitor.md index c383d54c0..0e714f7d1 100644 --- a/specs/DiagnosticMonitor.md +++ b/specs/DiagnosticMonitor.md @@ -401,14 +401,14 @@ interface ICoreWebView2DiagnosticReceivedEventArgs : IUnknown { COREWEBVIEW2_DIAGNOSTIC_SCOPE* value); /// The wall-clock time at which the runtime observed this - /// diagnostic event, as the number of seconds since the - /// UNIX epoch (1970-01-01T00:00:00Z, UTC). Use this value - /// to correlate diagnostic events with other timestamped - /// telemetry. The value is derived from the system clock - /// and may be affected by clock adjustments (for example, - /// NTP). + /// diagnostic event, as the number of milliseconds since + /// the UNIX epoch (1970-01-01T00:00:00Z, UTC). Use this + /// value to correlate diagnostic events with other + /// timestamped telemetry. The value is derived from the + /// system clock and may be affected by clock adjustments + /// (for example, NTP). [propget] HRESULT Timestamp( - [out, retval] double* value); + [out, retval] INT64* value); /// Returns category-specific diagnostic data as a JSON /// string. The schema for each category is documented on @@ -626,7 +626,7 @@ namespace Microsoft.Web.WebView2.Core { CoreWebView2DiagnosticCategory Category { get; }; CoreWebView2DiagnosticScope Scope { get; }; - Windows.Foundation.DateTime Timestamp { get; }; + Int64 Timestamp { get; }; /// Returns category-specific data as a JSON /// string. The schema for each category is