Skip to content

Commit 45f9b92

Browse files
authored
Merge pull request #648 from MicrosoftEdge/api-browserprocessexited-draft
API Spec for BrowserProcessExited event API
2 parents 324cacd + de31420 commit 45f9b92

1 file changed

Lines changed: 322 additions & 0 deletions

File tree

specs/BrowserProcessExited.md

Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
# Background
2+
We have heard asks for a WebView2 API to easily track the WebView2 Runtime's
3+
browser process exit. Manually waiting for the process to exit requires
4+
additional work on the host app, so we are proposing the `BrowserProcessExited`
5+
event. The `ProcessFailed` event already lets app developers handle unexpected
6+
browser process exits for a WebView, this new API lets you listen to both
7+
expected and unexpected termination of the processes associated to an
8+
environment from the `ICoreWebView2Environment3` interface so you can, e.g.,
9+
cleanup the user data folder when it's no longer in use. In this document we
10+
describe the new API. We'd appreciate your feedback.
11+
12+
13+
# Description
14+
The `BrowserProcessExited` event allows developers to subscribe event handlers
15+
to be run when the WebView2 Runtime processes (including the browser process)
16+
associated to a `CoreWebView2Environment` terminate. Key scenarios are cleanup
17+
of the user data folder used by the WebView2 Runtime, which is locked while the
18+
runtime's browser process is active, and moving to a new WebView2 Runtime
19+
version after a `NewBrowserVersionAvailable` event.
20+
21+
This event is raised for both expected and unexpected browser process
22+
termination, after all resources, including the user data folder, used by the
23+
browser process (and related processes) have been released. The
24+
`ICoreWebView2BrowserProcessExitedEventArgs` interface lets app developers get
25+
the `BrowserProcessExitKind` so they can decide how to handle different exit
26+
kinds or bypass handling if an event handler for the `CoreWebView2`s
27+
`ProcessFailed` event (for `CoreWebView2ProcessFailedKind.BrowserProcessFailed`)
28+
is already registered. In case of a browser process crash, both
29+
`BrowserProcessExited` and `ProcessFailed` events are raised, but the order is
30+
not guaranteed.
31+
32+
All `CoreWebView2Environment` objects across different app processes that use
33+
the same browser process receive this event when the browser process (and
34+
associated processes) exits. If the browser process (and therefore the user data
35+
folder) in use by the app process (through the `CoreWebView2Environment` options
36+
used) is shared with other processes, these processes need to coordinate to
37+
handle the potential race condition on the use of the resources. E.g., if one
38+
app process tries to clear the user data folder, while other tries to recreate
39+
its WebViews on crash.
40+
41+
42+
# Examples
43+
The following code snippets demonstrate how the `BrowserProcessExited` event can
44+
be used:
45+
46+
## Win32 C++
47+
```cpp
48+
// Before closing the WebView, register a handler with code to run once the
49+
// browser process is terminated.
50+
EventRegistrationToken browserExitedEventToken;
51+
52+
CHECK_FAILURE(m_webViewEnvironment->add_BrowserProcessExited(
53+
Callback<ICoreWebView2BrowserProcessExitedEventHandler>(
54+
[browserExitedEventToken, this](
55+
ICoreWebView2Environment* sender,
56+
ICoreWebView2BrowserProcessExitedEventArgs* args) {
57+
COREWEBVIEW2_BROWSER_PROCESS_EXIT_KIND kind;
58+
CHECK_FAILURE(args->get_BrowserProcessExitKind(&kind));
59+
60+
// Watch for graceful browser process exit. Let ProcessFailed event
61+
// handler take care of failed browser process termination.
62+
if (kind == COREWEBVIEW2_BROWSER_PROCESS_EXIT_KIND_NORMAL)
63+
{
64+
CHECK_FAILURE(
65+
m_webViewEnvironment->remove_BrowserProcessExited(browserExitedEventToken));
66+
// Release the environment only after the handler is invoked.
67+
// Otherwise, there will be no environment to raise the event when
68+
// the process exits.
69+
m_webViewEnvironment = nullptr;
70+
CleanupUserDataFolder();
71+
}
72+
73+
return S_OK;
74+
}).Get(),
75+
&browserExitedEventToken));
76+
```
77+
78+
## .NET C#
79+
```c#
80+
// URI or other state to save/restore when the WebView is recreated.
81+
private Uri _uriToRestore;
82+
83+
async void RegisterForNewVersion()
84+
{
85+
// We need to make sure the CoreWebView2 property is not null, so we can get
86+
// the environment from it. Alternatively, if the WebView was created from
87+
// an environment provided to the control, we can use that environment
88+
// object directly.
89+
await webView.EnsureCoreWebView2Async();
90+
_coreWebView2Environment = webView.CoreWebView2.Environment;
91+
_coreWebView2Environment.NewBrowserVersionAvailable += Environment_NewBrowserVersionAvailable;
92+
}
93+
94+
// A new version of the WebView2 Runtime is available, our handler gets called.
95+
// We close our WebView and set a handler to reinitialize it once the browser
96+
// process is gone, so we get the new version of the WebView2 Runtime.
97+
void Environment_NewBrowserVersionAvailable(object sender, object e)
98+
{
99+
StringBuilder messageBuilder = new StringBuilder(256);
100+
messageBuilder.Append("We detected there is a new version of the WebView2 Runtime installed. ");
101+
messageBuilder.Append("Do you want to switch to it now? This will re-create the WebView.");
102+
var selection = MessageBox.Show(this, messageBuilder.ToString(), "New WebView2 Runtime detected", MessageBoxButton.YesNo);
103+
if (selection == MessageBoxResult.Yes)
104+
{
105+
// Save URI or other state you want to restore when the WebView is recreated.
106+
_uriToRestore = webView.Source;
107+
_coreWebView2Environment.BrowserProcessExited += Environment_BrowserProcessExited;
108+
// We dispose of the control so the internal WebView objects are released
109+
// and the associated browser process exits. If there are any other WebViews
110+
// from the same environment configuration, they need to be closed too.
111+
webView.Dispose();
112+
webView = null;
113+
}
114+
}
115+
116+
void Environment_BrowserProcessExited(object sender, CoreWebView2BrowserProcessExitedEventArgs e)
117+
{
118+
((CoreWebView2Environment)sender).BrowserProcessExited -= Environment_BrowserProcessExited;
119+
ReinitializeWebView();
120+
}
121+
122+
void ReinitializeWebView()
123+
{
124+
webView = new WebView2();
125+
126+
// Restore URI and other WebView state/setup.
127+
webView.CreationProperties = (CoreWebView2CreationProperties)this.FindResource("EvergreenWebView2CreationProperties");
128+
webView.NavigationStarting += WebView_NavigationStarting;
129+
webView.NavigationCompleted += WebView_NavigationCompleted;
130+
131+
Binding urlBinding = new Binding()
132+
{
133+
Source = webView,
134+
Path = new PropertyPath("Source"),
135+
Mode = BindingMode.OneWay
136+
};
137+
url.SetBinding(TextBox.TextProperty, urlBinding);
138+
139+
MyWindow.MyDockPanel.Children.Add(webView);
140+
webView.Source = (_uriToRestore != null) ? _uriToRestore : new Uri("https://www.bing.com");
141+
RegisterForNewVersion();
142+
}
143+
```
144+
145+
146+
# Remarks
147+
Note this is an event from the `ICoreWebView2Environment3` interface, not the
148+
`ICoreWebView2`. The difference between this `BrowserProcessExited` event and
149+
the `CoreWebView2`'s `ProcessFailed` event is that `BrowserProcessExited` is
150+
raised for any (expected and unexpected) **browser process** (along its
151+
associated processes) exits, while `ProcessFailed` is raised only for
152+
**unexpected** browser process exits, or for **render process**
153+
exits/unresponsiveness. To learn more about the WebView2 Process Model, go to
154+
[Process model](https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/process-model).
155+
156+
In the case the browser process crashes, both `BrowserProcessExited` and
157+
`ProcessFailed` events are raised, but the order is not guaranteed.
158+
159+
160+
# API Notes
161+
See [API Details](#api-details) section below for API reference.
162+
163+
164+
# API Details
165+
166+
## COM
167+
```cpp
168+
library WebView2
169+
{
170+
// ...
171+
172+
/// Specifies the browser process exit type used in the
173+
/// `ICoreWebView2BrowserProcessExitedEventArgs` interface.
174+
typedef enum COREWEBVIEW2_BROWSER_PROCESS_EXIT_KIND {
175+
/// Indicates that the browser process ended normally.
176+
COREWEBVIEW2_BROWSER_PROCESS_EXIT_KIND_NORMAL,
177+
178+
/// Indicates that the browser process ended unexpectedly.
179+
/// A `ProcessFailed` event will also be sent to listening WebViews from the
180+
/// `ICoreWebView2Environment` associated to the failed process.
181+
COREWEBVIEW2_BROWSER_PROCESS_EXIT_KIND_FAILED
182+
} COREWEBVIEW2_BROWSER_PROCESS_EXIT_KIND;
183+
184+
interface ICoreWebView2Environment3 : ICoreWebView2Environment2
185+
{
186+
// ...
187+
188+
/// Add an event handler for the `BrowserProcessExited` event.
189+
/// The `BrowserProcessExited` event is raised when the browser process of the
190+
/// WebView2 Runtime associated to this environment terminates due to an error
191+
/// or normal shutdown (e.g., when all its WebViews are closed), after all
192+
/// resources (including the user data folder) used by the browser process
193+
/// (and related processes) have been released.
194+
///
195+
/// A handler added with this method is called until removed with
196+
/// `remove_BrowserProcessExited`, even if a new browser process is bound to
197+
/// this environment after earlier `BrowserProcessExited` events are raised.
198+
///
199+
/// All `CoreWebView2Environment` objects across different app processes that use
200+
/// the same browser process receive this event when the browser process (and
201+
/// associated processes) exits. If the browser process (and therefore the user data
202+
/// folder) in use by the app process (through the `CoreWebView2Environment` options
203+
/// used) is shared with other processes, these processes need to coordinate to
204+
/// handle the potential race condition on the use of the resources. E.g., if one
205+
/// app process tries to clear the user data folder, while other tries to recreate
206+
/// its WebViews on crash.
207+
///
208+
/// Note this is an event from the `ICoreWebView2Environment3` interface, not the
209+
/// `ICoreWebView2`. The difference between this `BrowserProcessExited` event and
210+
/// the `CoreWebView2`'s `ProcessFailed` event is that `BrowserProcessExited` is
211+
/// raised for any (expected and unexpected) **browser process** (along its
212+
/// associated processes) exits, while `ProcessFailed` is raised only for
213+
/// **unexpected** browser process exits, or for **render process**
214+
/// exits/unresponsiveness. To learn more about the WebView2 Process Model, go to
215+
/// [Process model](https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/process-model).
216+
///
217+
/// In the case the browser process crashes, both `BrowserProcessExited` and
218+
/// `ProcessFailed` events are raised, but the order is not guaranteed.
219+
HRESULT add_BrowserProcessExited(
220+
[in] ICoreWebView2BrowserProcessExitedEventHandler* eventHandler,
221+
[out] EventRegistrationToken* token);
222+
223+
/// Remove an event handler previously added with `add_BrowserProcessExited`.
224+
HRESULT remove_BrowserProcessExited([in] EventRegistrationToken token);
225+
}
226+
227+
/// Receives `BrowserProcessExited` events.
228+
interface ICoreWebView2BrowserProcessExitedEventHandler : IUnknown
229+
{
230+
/// Provides the event args for the corresponding event.
231+
HRESULT Invoke(
232+
[in] ICoreWebView2Environment* sender,
233+
[in] ICoreWebView2BrowserProcessExitedEventArgs* args);
234+
}
235+
236+
/// Event args for the `BrowserProcessExited` event.
237+
interface ICoreWebView2BrowserProcessExitedEventArgs : IUnknown
238+
{
239+
/// The kind of browser process exit that has occurred.
240+
[propget] HRESULT BrowserProcessExitKind(
241+
[out, retval] COREWEBVIEW2_BROWSER_PROCESS_EXIT_KIND* browserProcessExitKind);
242+
}
243+
```
244+
245+
## .NET and WinRT
246+
```c#
247+
namespace Microsoft.Web.WebView2.Core
248+
{
249+
// ...
250+
251+
/// Specifies the browser process exit kind used in
252+
/// `CoreWebView2BrowserProcessExitedEventArgs`.
253+
enum CoreWebView2BrowserProcessExitKind
254+
{
255+
/// Indicates that the browser process ended normally.
256+
Normal,
257+
/// Indicates that the browser process ended unexpectedly.
258+
/// A `CoreWebView2.ProcessFailed` event will also be raised to
259+
/// listening WebViews from the `CoreWebView2Environment` associated to
260+
/// the failed process.
261+
Failed
262+
};
263+
264+
runtimeclass CoreWebView2Environment
265+
{
266+
// ...
267+
268+
/// `BrowserProcessExited` is raised when the browser process of the
269+
/// WebView2 Runtime associated to this `CoreWebView2Environment`
270+
/// terminates due to an error or normal shutdown (e.g., when all its
271+
/// WebViews are closed), after all resources (including the user data
272+
/// folder) used by the browser process (and related processes) have
273+
/// been released.
274+
///
275+
/// All `CoreWebView2Environment` objects across different app processes that use
276+
/// the same browser process receive this event when the browser process (and
277+
/// associated processes) exits. If the browser process (and therefore the user data
278+
/// folder) in use by the app process (through the `CoreWebView2Environment` options
279+
/// used) is shared with other processes, these processes need to coordinate to
280+
/// handle the potential race condition on the use of the resources. E.g., if one
281+
/// app process tries to clear the user data folder, while other tries to recreate
282+
/// its WebViews on crash.
283+
///
284+
/// Note this is an event from the `ICoreWebView2Environment3` interface, not the
285+
/// `ICoreWebView2`. The difference between this `BrowserProcessExited` event and
286+
/// the `CoreWebView2`'s `ProcessFailed` event is that `BrowserProcessExited` is
287+
/// raised for any (expected and unexpected) **browser process** (along its
288+
/// associated processes) exits, while `ProcessFailed` is raised only for
289+
/// **unexpected** browser process exits, or for **render process**
290+
/// exits/unresponsiveness. To learn more about the WebView2 Process Model, go to
291+
/// [Process model](https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/process-model).
292+
///
293+
/// In the case the browser process crashes, both `BrowserProcessExited` and
294+
/// `ProcessFailed` events are raised, but the order is not guaranteed.
295+
event Windows.Foundation.TypedEventHandler<CoreWebView2Environment, CoreWebView2BrowserProcessExitedEventArgs> BrowserProcessExited;
296+
}
297+
298+
/// Event args for the `CoreWebView2Environment.BrowserProcessExited` event.
299+
runtimeclass CoreWebView2BrowserProcessExitedEventArgs
300+
{
301+
/// The kind of browser process exit that has occurred.
302+
CoreWebView2BrowserProcessExitKind BrowserProcessExitKind { get; };
303+
}
304+
}
305+
```
306+
307+
# Appendix
308+
We expect that for the two scenarios this API is designed for, namely cleanup of
309+
the user data folder and upgrading the WebView2 Runtime, an app adding a
310+
handler for `BrowserProcessExited` will only be interested in the next single
311+
time the browser process exits (even if there could be more browser processes
312+
being created and exiting throughout the lifetime of a
313+
`CoreWebView2Environment`). For this reason, we also consider making this event
314+
an async method instead (e.g., `RegisterWaitForBrowserProcessExit`).
315+
316+
While there would be no operation started on calling the async method, a handler
317+
would be a added to be run (only) the next time the browser process associated
318+
to the `CoreWebView2Environment` exits, which in turn would make API usage
319+
easier for the two expected scenarios.
320+
321+
Alternatively, this could be kept an event and the registered handlers be
322+
automatically removed the next time the event is raised.

0 commit comments

Comments
 (0)