@@ -5,7 +5,7 @@ additional work on the host app, so we are proposing the `BrowserProcessExited`
55event. The ` ProcessFailed ` event already lets app developers handle unexpected
66browser process exits for a WebView; this new API lets you listen to both
77expected and unexpected termination of the collection of processes associated to
8- a ` CoreWebView2Environment ` so you can, e.g. , cleanup the user data folder when
8+ a ` CoreWebView2Environment ` so you can, for example , cleanup the user data folder when
99it's no longer in use. In this document we describe the new API. We'd appreciate
1010your feedback.
1111
@@ -19,7 +19,7 @@ process is active, and moving to a new WebView2 Runtime version after a
1919` NewBrowserVersionAvailable ` event.
2020
2121This event is raised for both expected and unexpected termination of the
22- collection of processes, after all resources, including the user data folder,
22+ collection of processes, after all resources, including the user data folder,
2323used by the WebView2 Runtime have been released. The
2424` CoreWebView2BrowserProcessExitedEventArgs ` lets app developers get
2525the ` BrowserProcessExitKind ` so they can decide how to handle different exit
@@ -32,14 +32,17 @@ different scenarios. It is up to the app to coordinate the handlers so they do
3232not try to perform reliability recovery while also trying to move to a new
3333WebView2 Runtime version or remove the user data folder.
3434
35- Multiple app processes can share a browser process by creating their
36- ` CoreWebView2Environment ` with the same user data folder. When the entire
37- collection of WebView2Runtime processes sharing the browser process exit, all
38- associated ` CoreWebview2Environment ` objects receive the ` BrowserProcessExited `
35+ Multiple app processes can share a browser process by creating their webviews
36+ from a ` CoreWebView2Environment ` with the same user data folder. When the entire
37+ collection of WebView2Runtime processes for the browser process exit, all
38+ associated ` CoreWebView2Environment ` objects receive the ` BrowserProcessExited `
3939event. Multiple processes sharing the same browser process need to coordinate
40- their use of the shared user data folder to avoid race conditions. For example,
41- one process should not clear the user data folder at the same time that another
42- process recovers from a crash by recreating its WebView controls.
40+ their use of the shared user data folder to avoid race conditions and
41+ unnecessary waits. For example, one process should not clear the user data
42+ folder at the same time that another process recovers from a crash by recreating
43+ its WebView controls; one process should not block waiting for the event if
44+ other app processes are using the same browser process (the browser process will
45+ not exit until those other processes have closed their webviews too).
4346
4447
4548# Examples
@@ -53,6 +56,24 @@ class AppWindow {
5356
5457 wil::com_ptr<ICoreWebView2Controller > m_controller;
5558 EventRegistrationToken m_browserExitedEventToken = {};
59+ UINT32 m_newestBrowserPid = 0;
60+ }
61+
62+ HRESULT AppWindow::OnCreateCoreWebView2ControllerCompleted(HRESULT result, ICoreWebView2Controller* controller)
63+ {
64+ if (result == S_OK)
65+ {
66+ // ...
67+
68+ // Save PID of the browser process serving last WebView created from our
69+ // CoreWebView2Environment. We know the controller was created with
70+ // S_OK, and it hasn't been closed (we haven't called Close and no
71+ // ProcessFailed event could have been raised yet) so the PID is
72+ // available.
73+ CHECK_FAILURE (m_webView->get_BrowserProcessId(&m_newestBrowserPid));
74+
75+ // ...
76+ }
5677}
5778
5879void AppWindow::CloseWebView(/* ... * /) {
@@ -66,25 +87,49 @@ void AppWindow::CloseWebView(/* ... */) {
6687 ICoreWebView2Environment* sender,
6788 ICoreWebView2BrowserProcessExitedEventArgs* args) {
6889 COREWEBVIEW2_BROWSER_PROCESS_EXIT_KIND kind;
90+ UINT32 pid;
6991 CHECK_FAILURE(args->get_BrowserProcessExitKind(&kind));
70-
71- // Watch for graceful browser process exit. Let ProcessFailed event
72- // handler take care of failed browser process termination.
73- if (kind == COREWEBVIEW2_BROWSER_PROCESS_EXIT_KIND_NORMAL)
92+ CHECK_FAILURE(args->get_BrowserProcessId(&pid));
93+
94+ // If a new WebView is created from this CoreWebView2Environment after
95+ // the browser has exited but before our handler gets to run, a new
96+ // browser process will be created and lock the user data folder
97+ // again. Do not attempt to cleanup the user data folder in these
98+ // cases. If this happens, the PID of the new browser process will be
99+ // different to the PID of the older process, we check against the
100+ // PID of the browser process to which our last CoreWebView2 attached.
101+ if (pid == m_newestBrowserPid)
102+ {
103+ // Watch for graceful browser process exit. Let ProcessFailed event
104+ // handler take care of failed browser process termination.
105+ if (kind == COREWEBVIEW2_BROWSER_PROCESS_EXIT_KIND_NORMAL)
106+ {
107+ CHECK_FAILURE(
108+ m_webViewEnvironment->remove_BrowserProcessExited(m_browserExitedEventToken));
109+ // Release the environment only after the handler is invoked.
110+ // Otherwise, there will be no environment to raise the event when
111+ // the collection of WebView2 Runtime processes exit.
112+ m_webViewEnvironment = nullptr;
113+ CleanupUserDataFolder();
114+ }
115+ }
116+ else
74117 {
75- CHECK_FAILURE(
76- m_webViewEnvironment->remove_BrowserProcessExited(m_browserExitedEventToken));
77- // Release the environment only after the handler is invoked.
78- // Otherwise, there will be no environment to raise the event when
79- // the collection of WebView2 Runtime processes exit.
80- m_webViewEnvironment = nullptr;
81- CleanupUserDataFolder();
118+ // The exiting process is not the last in use. Do not attempt cleanup
119+ // as we might still have a webview open over the user data folder.
120+ // Do not block from event handler.
121+ RunAsync([this]() {
122+ MessageBox(
123+ m_mainWindow,
124+ L"A new browser process prevented cleanup of the user data folder.",
125+ L"Cleanup User Data Folder", MB_OK);
126+ });
82127 }
83128
84129 return S_OK;
85130 }).Get(),
86131 &m_browserExitedEventToken));
87-
132+
88133 // ...
89134
90135 // Close the WebView from its controller.
@@ -117,7 +162,7 @@ void AppWindow::CloseWebView(/* ... */) {
117162 <!-- ... -->
118163
119164 <DockPanel DockPanel.Dock="Top">
120-
165+
121166 <!-- ... -->
122167
123168 <TextBox x:Name="url" Text="{Binding ElementName=webView,Path=Source,Mode=OneWay}">
@@ -227,12 +272,13 @@ void ReinitializeWebView()
227272
228273# Remarks
229274Note this is an event from ` CoreWebView2Environment ` , not ` CoreWebView2 ` . The
230- difference between this ` BrowserProcessExited ` event and the ` CoreWebView2 ` 's
231- ` ProcessFailed ` event is that ` BrowserProcessExited ` is raised for any (expected
232- and unexpected) ** browser process** (along its associated processes) exits,
233- while ` ProcessFailed ` is raised only for ** unexpected** browser process exits,
234- or for ** render process** exits/unresponsiveness. To learn more about the
235- WebView2 Process Model, go to [ Process model] ( https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/process-model ) .
275+ difference between ` BrowserProcessExited ` and ` CoreWebView2 ` 's ` ProcessFailed `
276+ is that ` BrowserProcessExited ` is raised for any ** browser process** exit
277+ (expected or unexpected, after all associated processes have exited too), while
278+ ` ProcessFailed ` is raised for ** unexpected** process exits of any kind (browser,
279+ render, GPU, and all other types), or for main frame ** render process**
280+ unresponsiveness. To learn more about the WebView2 Process Model, go to
281+ [ Process model] ( https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/process-model ) .
236282
237283In the case the browser process crashes, both ` BrowserProcessExited ` and
238284` ProcessFailed ` events are raised, but the order is not guaranteed.
@@ -268,31 +314,36 @@ interface ICoreWebView2Environment3 : ICoreWebView2Environment2
268314
269315 /// Add an event handler for the ` BrowserProcessExited ` event.
270316 /// The ` BrowserProcessExited ` event is raised when the collection of WebView2
271- /// Runtime processes associated to this environment terminate due to a
272- /// browser process error or normal shutdown (e.g., when all associated
273- /// WebViews are closed), after all resources (including the user data folder)
274- /// have been released.
317+ /// Runtime processes for the browser process of this environment terminate
318+ /// due to browser process failure or normal shutdown (for example, when all
319+ /// associated WebViews are closed), after all resources have been released
320+ /// (including the user data folder). To learn about what these processes are,
321+ /// go to [ Process model] ( https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/process-model ) .
275322 ///
276323 /// A handler added with this method is called until removed with
277324 /// ` remove_BrowserProcessExited ` , even if a new browser process is bound to
278325 /// this environment after earlier ` BrowserProcessExited ` events are raised.
279326 ///
280- /// Multiple app processes can share a browser process by creating their
281- /// ` ICoreWebView2Environment ` with the same user data folder. When the entire
282- /// collection of WebView2Runtime processes sharing the browser process exit, all
283- /// associated ` ICoreWebview2Environment ` objects receive the ` BrowserProcessExited `
327+ /// Multiple app processes can share a browser process by creating their webviews
328+ /// from a ` ICoreWebView2Environment ` with the same user data folder. When the entire
329+ /// collection of WebView2Runtime processes for the browser process exit, all
330+ /// associated ` ICoreWebView2Environment ` objects receive the ` BrowserProcessExited `
284331 /// event. Multiple processes sharing the same browser process need to coordinate
285- /// their use of the shared user data folder to avoid race conditions. For example,
286- /// one process should not clear the user data folder at the same time that another
287- /// process recovers from a crash by recreating its WebView controls.
332+ /// their use of the shared user data folder to avoid race conditions and
333+ /// unnecessary waits. For example, one process should not clear the user data
334+ /// folder at the same time that another process recovers from a crash by recreating
335+ /// its WebView controls; one process should not block waiting for the event if
336+ /// other app processes are using the same browser process (the browser process will
337+ /// not exit until those other processes have closed their webviews too).
288338 ///
289- /// Note this is an event from the ` ICoreWebView2Environment3 ` interface, not the
290- /// ` ICoreWebView2 ` . The difference between this ` BrowserProcessExited ` event and
291- /// the ` ICoreWebView2 ` 's ` ProcessFailed ` event is that ` BrowserProcessExited ` is
292- /// raised for any (expected and unexpected) ** browser process** (along its
293- /// associated processes) exits, while ` ProcessFailed ` is raised only for
294- /// ** unexpected** browser process exits, or for ** render process**
295- /// exits/unresponsiveness. To learn more about the WebView2 Process Model, go to
339+ /// Note this is an event from the ` ICoreWebView2Environment3 ` interface, not
340+ /// the ` ICoreWebView2 ` one. The difference between ` BrowserProcessExited ` and
341+ /// ` ICoreWebView2 ` 's ` ProcessFailed ` is that ` BrowserProcessExited ` is
342+ /// raised for any ** browser process** exit (expected or unexpected, after all
343+ /// associated processes have exited too), while ` ProcessFailed ` is raised for
344+ /// ** unexpected** process exits of any kind (browser, render, GPU, and all
345+ /// other types), or for main frame ** render process** unresponsiveness. To
346+ /// learn more about the WebView2 Process Model, go to
296347 /// [ Process model] ( https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/process-model ) .
297348 ///
298349 /// In the case the browser process crashes, both ` BrowserProcessExited ` and
@@ -324,6 +375,8 @@ interface ICoreWebView2BrowserProcessExitedEventArgs : IUnknown
324375 /// The kind of browser process exit that has occurred.
325376 [ propget] HRESULT BrowserProcessExitKind(
326377 [ out, retval] COREWEBVIEW2_BROWSER_PROCESS_EXIT_KIND* browserProcessExitKind);
378+ /// The process ID of the browser process that has exited.
379+ [ propget] HRESULT BrowserProcessId([ out, retval] UINT32* value);
327380}
328381```
329382
@@ -351,26 +404,31 @@ namespace Microsoft.Web.WebView2.Core
351404 // ...
352405
353406 /// `BrowserProcessExited` is raised when the collection of WebView2
354- /// Runtime processes associated to this `CoreWebView2Environment` terminate due to a
355- /// browser process error or normal shutdown (e.g., when all associated
356- /// WebViews are closed), after all resources (including the user data folder)
357- /// have been released.
407+ /// Runtime processes for the browser process of this `CoreWebView2Environment`
408+ /// terminate due to browser process failure or normal shutdown (for example,
409+ /// when all associated WebViews are closed), after all resources have been
410+ /// released (including the user data folder). To learn about what these processes
411+ /// are, go to [Process model](https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/process-model).
358412 ///
359- /// Multiple app processes can share a browser process by creating their
360- /// `CoreWebView2Environment` with the same user data folder. When the entire
361- /// collection of WebView2Runtime processes sharing the browser process exit, all
362- /// associated `CoreWebview2Environment ` objects receive the `BrowserProcessExited`
413+ /// Multiple app processes can share a browser process by creating their webviews
414+ /// from a `CoreWebView2Environment` with the same user data folder. When the entire
415+ /// collection of WebView2Runtime processes for the browser process exit, all
416+ /// associated `CoreWebView2Environment ` objects receive the `BrowserProcessExited`
363417 /// event. Multiple processes sharing the same browser process need to coordinate
364- /// their use of the shared user data folder to avoid race conditions. For example,
365- /// one process should not clear the user data folder at the same time that another
366- /// process recovers from a crash by recreating its WebView controls.
418+ /// their use of the shared user data folder to avoid race conditions and
419+ /// unnecessary waits. For example, one process should not clear the user data
420+ /// folder at the same time that another process recovers from a crash by recreating
421+ /// its WebView controls; one process should not block waiting for the event if
422+ /// other app processes are using the same browser process (the browser process will
423+ /// not exit until those other processes have closed their webviews too).
367424 ///
368425 /// Note this is an event from `CoreWebView2Environment`, not `CoreWebView2`. The
369- /// difference between this `BrowserProcessExited` event and the `CoreWebView2`'s
370- /// `ProcessFailed` event is that `BrowserProcessExited` is raised for any (expected
371- /// and unexpected) **browser process** (along its associated processes) exits, while
372- /// `ProcessFailed` is raised only for **unexpected** browser process exits, or for
373- /// **render process** exits/unresponsiveness. To learn more about the WebView2
426+ /// difference between `BrowserProcessExited` and `CoreWebView2`'s
427+ /// `ProcessFailed` is that `BrowserProcessExited` is raised for any **browser process** exit
428+ /// (expected or unexpected, after all associated processes have exited too), while
429+ /// `ProcessFailed` is raised for **unexpected** process exits of any kind (browser,
430+ /// render, GPU, and all other types), or for main frame **render process**
431+ /// unresponsiveness. To learn more about the WebView2
374432 /// Process Model, go to [Process model](https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/process-model).
375433 ///
376434 /// In the case the browser process crashes, both `BrowserProcessExited` and
@@ -387,6 +445,9 @@ namespace Microsoft.Web.WebView2.Core
387445 {
388446 /// The kind of browser process exit that has occurred.
389447 CoreWebView2BrowserProcessExitKind BrowserProcessExitKind { get ; };
448+
449+ /// The process ID of the browser process that has exited.
450+ UInt32 BrowserProcessId { get ; };
390451 }
391452}
392453```
@@ -398,7 +459,7 @@ handler for `BrowserProcessExited` will only be interested in the next single
398459time the browser process exits (even if there could be more browser processes
399460being created and exiting throughout the lifetime of a
400461` CoreWebView2Environment ` ). For this reason, we also consider making this event
401- an async method instead (e.g. , ` RegisterWaitForBrowserProcessExit ` ).
462+ an async method instead (for example , ` RegisterWaitForBrowserProcessExit ` ).
402463
403464While there would be no operation started on calling the async method, a handler
404465would be a added to be run (only) the next time the browser process associated
0 commit comments