From f3549398e63cf02d339d56ac88b9a5eec7961d91 Mon Sep 17 00:00:00 2001 From: Wangsong Jin Date: Wed, 22 Feb 2023 09:31:51 -0800 Subject: [PATCH] Add iframe memory usage API Add iframe memory usage API --- specs/IFrameMemoryUsage.md | 306 +++++++++++++++++++++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 specs/IFrameMemoryUsage.md diff --git a/specs/IFrameMemoryUsage.md b/specs/IFrameMemoryUsage.md new file mode 100644 index 0000000..27ae9c5 --- /dev/null +++ b/specs/IFrameMemoryUsage.md @@ -0,0 +1,306 @@ +IFrame Memory Usage API +=== + +# Background +The WebView2 team has been asked to provide support for monitoring iframe +memory usage. The end developer needs to know how much memory each iframe +consumes. To do that, they need to get the renderer process ID for each +iframe. However, WebView2 only tracks the render frame host for top-level iframes. +This limits the [ICoreWebView2Frame2](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2frame?view=webview2-1.0.1518.46) +APIs for first-level iframes only. For instance, we raise the FrameCreated +event for first-level iframes, but not for nested iframes with depth greater than 1. +Also, WebView2 lacks an API to get the renderer process ID for iframes. +
+                           main_frame  [depth:0]
+                         /     |       \
+      first_level_iframe_0     ...    first_level_iframe_n             [depth:1, first level iframe]
+                |          \                     |
+      second_level_iframe_0 ...       second_level_iframe_n ...        [depth:2, second level iframe]
+               |
+              ...                                                      
+
+ +In this document we describe the updated API. We'd appreciate your feedback. + +# Description +We propose extending `CoreWebView2` and `CoreWebView2Frame` to include the +`RendererProcessId` property and `RendererProcessIdChanged` event. This property +stores the renderer process ID for its iframe. This event will be raised whenever +the renderer process ID is changed for `CoreWebView2` or `CoreWebView2Frame`. + +Additionally, we propose to add the `FrameCreated` event to `CoreWebView2Frame`. +This event is raised when a new iframe is created. + +# Examples +C++ +```c++ +void InitializeEventView(wil::com_ptr webview) +{ + auto webview2_18 = webview.try_query(); + if (webview2_18) + { + webview2_18->add_FrameCreated( + Callback( + [this](ICoreWebView2* sender, ICoreWebView2FrameCreatedEventArgs* args) + -> HRESULT { + // ... + // The new frame created event can be recursively initialized + InitializeFrameEventView(webviewFrame); + // ... + return S_OK; + }) + .Get(), + NULL); + //! [ICoreWebView2:add_RendererProcessIdChanged] + webview2_18->add_RendererProcessIdChanged( + Callback( + [this](ICoreWebView2* sender, IUnknown* args) -> HRESULT + { + wil::com_ptr webview2_18; + sender->QueryInterface(IID_PPV_ARGS(&webview2_18)); + UINT32 processId; + //! [ICoreWebView2:get_RendererProcessId] + CHECK_FAILURE(webview2_18->get_RendererProcessId(&processId)); + // Do something when renderer process id has been changed. + // For instance, retrieve the updated memory usage information + // for that main frame. + return S_OK; + }) + .Get(), + NULL); + } +} + +void InitializeFrameEventView( + wil::com_ptr webviewFrame) +{ + auto frame5 = webviewFrame.try_query(); + if (frame5) + { + //! [ICoreWebView2Frame:add_FrameCreated] + frame5->add_FrameCreated( + Callback( + [this]( + ICoreWebView2Frame* sender, + ICoreWebView2FrameCreatedEventArgs* args) -> HRESULT + { + wil::com_ptr webviewFrame; + CHECK_FAILURE(args->get_Frame(&webviewFrame)); + InitializeFrameEventView(webviewFrame); + auto frame5 = webviewFrame.try_query(); + if (frame5) + { + //! [ICoreWebView2Frame:get_RendererProcessId] + UINT32 processId; + CHECK_FAILURE(frame5->get_RendererProcessId(&processId)); + // Do something when renderer process id has been changed. + // For instance, retrieve the updated memory usage information + // for that iframe. + } + return S_OK; + }) + .Get(), + NULL); + //! [ICoreWebView2Frame:add_RendererProcessIdChanged] + frame5->add_RendererProcessIdChanged( + Callback( + [this]( + ICoreWebView2Frame* sender, + IUnknown* args) -> HRESULT + { + wil::com_ptr frame5; + sender->QueryInterface(IID_PPV_ARGS(&frame5)); + if (frame5) + { + //! [ICoreWebView2Frame:get_RendererProcessId] + UINT32 processId; + CHECK_FAILURE(frame5->get_RendererProcessId(&processId)); + // Do something when renderer process id has been changed. + // For instance, retrieve the updated memory usage information + // for that iframe. + } + return S_OK; + + }) + .Get(), + NULL); + } +``` +C# +```c# +void WebView_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e) +{ + if (e.IsSuccess) + { + // ... + webView.CoreWebView2.FrameCreated += WebView_HandleIFrames; + // + webView.CoreWebView2.RendererProcessIdChanged += WebView_RendererProcessIdChanged; + // ... + } +} +void WebView_HandleIFrames(object sender, CoreWebView2FrameCreatedEventArgs args) +{ + _webViewFrames.Add(args.Frame); + args.Frame.FrameCreated += WebViewFrames_CreatedNestedIFrames; + // + args.Frame.RendererProcessIdChanged += WebViewFrames_RendererProcessIdChanged; + // ... +} + +void WebViewFrames_CreatedNestedIFrames(object sender, CoreWebView2FrameCreatedEventArgs args) +{ + // Add frame created event for webViewFrames + _webViewFrames.Add(args.Frame); + // + args.Frame.FrameCreated += WebViewFrames_CreatedNestedIFrames; + // + args.Frame.RendererProcessIdChanged += WebViewFrames_RendererProcessIdChanged; + // ... +} + +void WebView_RendererProcessIdChanged(object sender, object args) +{ + // + var processId = webView.CoreWebView2.RendererProcessId; + // Do something when renderer process id has been changed. + // For instance, retrieve the updated memory usage information + // for that main frame. +} +void WebViewFrames_RendererProcessIdChanged(object sender, object args) +{ + bool isWebviewFrame = (sender is CoreWebView2Frame); + if (isWebviewFrame) + { + // + var processId = ((CoreWebView2Frame)sender).RendererProcessId; + // Do something when renderer process id has been changed. + // For instance, retrieve the updated memory usage information + // for that iframe. + } +} +``` + +# API Details +## C++ +```c++ +interface ICoreWebView2_18; +interface ICoreWebView2Frame5; +interface ICoreWebView2NestedFrameCreatedEventHandler; +interface ICoreWebView2RendererProcessIdChangedEventHandler; +interface ICoreWebView2FrameRendererProcessIdChangedEventHandler; + +/// Receives `FrameCreated` event. +// MSOWNERS: wangsongjin@microsoft.com +[uuid(c0e55260-a1bb-11ed-a8fc-0242ac120002), object, pointer_default(unique)] +interface ICoreWebView2NestedFrameCreatedEventHandler : IUnknown { + /// Provides the result for the iframe created event. + HRESULT Invoke([in] ICoreWebView2Frame* sender, + [in] ICoreWebView2FrameCreatedEventArgs* args); +} + +/// Receives `RendererProcessIdChanged` event. +// MSOWNERS: wangsongjin@microsoft.com +[uuid(012a41fe-a41f-11ed-a8fc-0242ac120002), object, pointer_default(unique)] +interface ICoreWebView2RendererProcessIdChangedEventHandler : IUnknown { + /// Provides the result for the process changed event. + /// No event args exist and the args parameter is set to null. + HRESULT Invoke([in] ICoreWebView2 * sender, + [in] IUnknown* args); +} + +/// Receives `RendererProcessIdChanged` event. +// MSOWNERS: wangsongjin@microsoft.com +[uuid(c1e0a3d2-a74b-11ed-afa1-0242ac120002), object, pointer_default(unique)] +interface ICoreWebView2FrameRendererProcessIdChangedEventHandler : IUnknown { + /// Provides the result for the process changed event. + HRESULT Invoke([in] ICoreWebView2Frame* sender, + [in] IUnknown* args); +} + +/// This is an extension of the ICoreWebView2Frame interface. +// MSOWNERS: wangsongjin@microsoft.com +[uuid(04baa798-a0e9-11ed-a8fc-0242ac120002), object, pointer_default(unique)] +interface ICoreWebView2Frame5 : ICoreWebView2Frame4 { + /// Raised when a new iframe is created. + /// Handle this event to get access to ICoreWebView2Frame objects. + /// Use ICoreWebView2Frame.add_Destroyed to listen for when this iframe goes + /// away. + // MSOWNERS: wangsongjin@microsoft.com + HRESULT add_FrameCreated( + [in] ICoreWebView2NestedFrameCreatedEventHandler* eventHandler, + [out] EventRegistrationToken* token); + + /// Remove an event handler previously added with add_FrameCreated. + // MSOWNERS: wangsongjin@microsoft.com + HRESULT remove_FrameCreated([in] EventRegistrationToken token); + + /// Get the renderer process id of the iframe. + /// The renderer process ID can change when navigating to a new document + /// and you can use the RendererProcessIdChanged event to know when it changes. + /// Child frames may have different renderer processes. + // MSOWNERS: wangsongjin@microsoft.com + [propget] HRESULT RendererProcessId([out, retval] UINT32* value); + + /// Subscribe to renderer process Id changed event. + // MSOWNERS: wangsongjin@microsoft.com + HRESULT add_RendererProcessIdChanged( + [in] ICoreWebView2FrameRendererProcessIdChangedEventHandler* eventHandler, + [out] EventRegistrationToken* token); + + /// Remove an event handler previously added with add_RendererProcessIdChanged. + // MSOWNERS: wangsongjin@microsoft.com + HRESULT remove_RendererProcessIdChanged([in] EventRegistrationToken token); +} + +/// A continuation of the `ICoreWebView2` interface to support get +/// renderer process id and handle render process change event +[uuid(ad712504-a66d-11ed-afa1-0242ac120002), object, pointer_default(unique)] +interface ICoreWebView2_18 : ICoreWebView2_17 { + /// Get the renderer process id of the main frame + /// The renderer process ID can change when navigating to a new document. + // + // MSOWNERS: wangsongjin@microsoft.com + [propget] HRESULT RendererProcessId([out, retval] UINT32* value); + + /// Subscribe to renderer process id changed event + // MSOWNERS: wangsongjin@microsoft.com + HRESULT add_RendererProcessIdChanged( + [in] ICoreWebView2RendererProcessIdChangedEventHandler* eventHandler, + [out] EventRegistrationToken* token); + + /// Remove an event handler previously added with add_RendererProcessIdChanged. + // MSOWNERS: wangsongjin@microsoft.com + HRESULT remove_RendererProcessIdChanged([in] EventRegistrationToken token); +} +``` + +C# +```c# (but really MIDL3) +namespace Microsoft.Web.WebView2.Core +{ + runtimeclass CoreWebView2 + { + [interface_name("Microsoft.Web.WebView2.Core.ICoreWebView2_18")] + { + // ICoreWebView2_18 members + UInt32 RendererProcessId { get; }; + event Windows.Foundation.TypedEventHandler RendererProcessIdChanged; + } + } + + runtimeclass CoreWebView2Frame + { + [interface_name("Microsoft.Web.WebView2.Core.ICoreWebView2Frame5")] + { + // ICoreWebView2Frame5 members + UInt32 RendererProcessId { get; }; + event Windows.Foundation.TypedEventHandler FrameCreated; + event Windows.Foundation.TypedEventHandler RendererProcessIdChanged; + } + } +} +``` + +# Appendix +See here for more details about the process model documentation: Here