@@ -5,8 +5,8 @@ CoreWebView2Frame.FrameCreated API
55At present, WebView2 enables developers to track only first-level
66iframes, which are the direct child iframes of the main frame.
77However, we see that WebView2 customers want to manage nested
8- iframes, such as recording the navigation history for a nested
9- iframe. To address this, we will introduce the
8+ iframes, such as recording the navigation history for a second
9+ level iframe. To address this, we will introduce the
1010` CoreWebView2Frame.FrameCreated ` API. This new API will allow
1111developers to subscribe to the nested iframe creation event,
1212giving them access to all properties, methods, and events of
@@ -15,7 +15,7 @@ for the nested iframe.
1515
1616To prevent unnecessary performance implication, WebView2 does
1717not track any nested iframes by default. It only tracks a nested
18- iframe if its parent iframe(` CoreWebView2Frame ` ) has subscribed
18+ iframe if its parent iframe (` CoreWebView2Frame ` ) has subscribed
1919to the ` CoreWebView2Frame.FrameCreated ` API. For a page with
2020multi-level iframes, developers can choose to track only the
2121main page and first-level iframes (the default behavior), a
@@ -25,209 +25,106 @@ or the full WebView2 frames tree.
2525# Examples
2626### C++ Sample
2727``` cpp
28- EventRegistrationToken m_frameCreatedToken = {};
29- void PostFrameCreatedEventMessage (wil::com_ptr<ICoreWebView2Frame > webviewFrame);
30-
31- void ScenarioWebViewEventMonitor::InitializeEventView(ICoreWebView2* webviewEventView)
32- {
33- auto webviewEventView4 = webviewEventView.try_query<ICoreWebView2_4>();
34- webviewEventView4->add_FrameCreated(
28+ wil::com_ptr<ICoreWebView2> m_webview;
29+ std::map<int , std::vector<std::wstring>> m_frame_navigation_urls;
30+ // In this example, a WebView2 application wants to manage the
31+ // navigation of third-party content residing in second-level iframes
32+ // (Main frame -> First-level frame -> Second-level third-party frames).
33+ HRESULT RecordThirdPartyFrameNavigation () {
34+ auto webview2_4 = m_webView.try_query<ICoreWebView2_4>();
35+ // Track the first-level webview frame.
36+ webview2_4->add_FrameCreated(
3537 Callback<ICoreWebView2FrameCreatedEventHandler>(
3638 [this](ICoreWebView2* sender, ICoreWebView2FrameCreatedEventArgs* args)
3739 -> HRESULT {
40+ // [AddFrameCreated]
3841 wil::com_ptr<ICoreWebView2Frame> webviewFrame;
3942 CHECK_FAILURE (args->get_Frame(&webviewFrame));
40- // Track first-level webview frame events.
41- InitializeFrameEventView(webviewFrame);
42- PostFrameCreatedEventMessage(webviewFrame);
43+ // Track nested (second-level) webview frame.
44+ auto frame7 = webviewFrame.try_query<ICoreWebView2Frame7 >();
45+ frame7->add_FrameCreated(
46+ Callback<ICoreWebView2FrameChildFrameCreatedEventHandler >(
47+ [ this] (
48+ ICoreWebView2Frame* sender,
49+ ICoreWebView2FrameCreatedEventArgs* args) -> HRESULT
50+ {
51+ wil::com_ptr<ICoreWebView2Frame > webviewFrame;
52+ CHECK_FAILURE(args->get_Frame(&webviewFrame));
53+ wil::com_ptr<ICoreWebView2Frame2 > frame2 =
54+ webviewFrame.try_query<ICoreWebView2Frame2 >();
55+ if (frame2)
56+ {
57+ // Subscribe to nested (second-level) webview frame navigation
58+ // starting event.
59+ frame2->add_NavigationStarting(
60+ Callback<ICoreWebView2FrameNavigationStartingEventHandler >(
61+ [ this] (
62+ ICoreWebView2Frame* sender,
63+ ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT {
64+ // Manage the navigation, e.g. cancel the
65+ // navigation if it's on block list.
66+ UINT32 frameId = 0;
67+ auto frame5 = wil::com_ptr<ICoreWebView2Frame >(sender)
68+ .try_query<ICoreWebView2Frame5 >();
69+ CHECK_FAILURE(frame5->get_FrameId(&frameId));
70+ wil::unique_cotaskmem_string uri;
71+ CHECK_FAILURE(args->get_Uri(&uri));
72+ // Log the navigation history per frame Id.
73+ m_frame_navigation_urls[ (int)frameId] .push_back(uri.get());
74+ return S_OK;
75+ })
76+ .Get(),
77+ nullptr);
78+ }
79+ return S_OK;
80+ })
81+ .Get(),
82+ nullptr);
83+ // [ AddFrameCreated]
4384 return S_OK;
4485 })
4586 .Get(),
46- &m_frameCreatedToken);
47- }
48-
49- // Track the creation, destruction, and navigation events of webview frames.
50- void ScenarioWebViewEventMonitor::InitializeFrameEventView(
51- wil::com_ptr<ICoreWebView2Frame > webviewFrame) {
52- auto frame7 = webviewFrame.try_query<ICoreWebView2Frame7 >();
53- if (frame7)
54- {
55- //! [ AddFrameCreated]
56- frame7->add_FrameCreated(
57- Callback<ICoreWebView2FrameNestedFrameCreatedEventHandler >(
58- [ this] (
59- ICoreWebView2Frame* sender,
60- ICoreWebView2FrameCreatedEventArgs* args) -> HRESULT
61- {
62- wil::com_ptr<ICoreWebView2Frame > webviewFrame;
63- CHECK_FAILURE(args->get_Frame(&webviewFrame));
64- // Make a recursive call to track all nested
65- // webview frame events.
66- InitializeFrameEventView(webviewFrame);
67- PostFrameCreatedEventMessage(webviewFrame);
68- return S_OK;
69- })
70- .Get(),
71- &m_frameCreatedToken);
72- //! [ AddFrameCreated]
73- }
74-
75- // Subscribe to webview frame destroyed event.
76- webviewFrame->add_Destroyed(
77- Callback<ICoreWebView2FrameDestroyedEventHandler>(
78- [this](ICoreWebView2Frame* sender, IUnknown* args) -> HRESULT
79- {
80- wil::unique_cotaskmem_string name;
81- CHECK_FAILURE(sender->get_Name(&name));
82- std::wstring message = L"{ \"kind\": \"event\", \"name\": "
83- L"\"CoreWebView2Frame::Destroyed\", \"args\": {";
84- message += L"\"frame name\": " + EncodeQuote(name.get());
85- message +=
86- L"}" + WebViewPropertiesToJsonString(m_webviewEventSource.get()) + L"}";
87- PostEventMessage(message);
88- return S_OK;
89- })
90- .Get(),
91- NULL);
92-
93- // Subscribe to webview frame navigation events.
94- wil::com_ptr<ICoreWebView2Frame2> frame2 = webviewFrame.try_query<ICoreWebView2Frame2>();
95- if (frame2)
96- {
97- frame2->add_NavigationStarting(
98- Callback<ICoreWebView2FrameNavigationStartingEventHandler>(
99- [this](
100- ICoreWebView2Frame* sender,
101- ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT {
102- std::wstring message = NavigationStartingArgsToJsonString(
103- m_webviewEventSource.get(), args,
104- L"CoreWebView2Frame::NavigationStarting");
105- PostEventMessage(message);
106- return S_OK;
107- })
108- .Get(),
109- NULL);
110-
111- frame2->add_ContentLoading(
112- Callback<ICoreWebView2FrameContentLoadingEventHandler>(
113- [this](ICoreWebView2Frame* sender, ICoreWebView2ContentLoadingEventArgs* args)
114- -> HRESULT {
115- std::wstring message = ContentLoadingArgsToJsonString(
116- m_webviewEventSource.get(), args, L"CoreWebView2Frame::ContentLoading");
117- PostEventMessage(message);
118- return S_OK;
119- })
120- .Get(),
121- NULL);
122-
123- frame2->add_DOMContentLoaded(
124- Callback<ICoreWebView2FrameDOMContentLoadedEventHandler>(
125- [this](ICoreWebView2Frame* sender, ICoreWebView2DOMContentLoadedEventArgs* args)
126- -> HRESULT {
127- std::wstring message = DOMContentLoadedArgsToJsonString(
128- m_webviewEventSource.get(), args,
129- L"CoreWebView2Frame::DOMContentLoaded");
130- PostEventMessage(message);
131- return S_OK;
132- })
133- .Get(),
134- NULL);
135-
136- frame2->add_NavigationCompleted(
137- Callback<ICoreWebView2FrameNavigationCompletedEventHandler>(
138- [this](
139- ICoreWebView2Frame* sender,
140- ICoreWebView2NavigationCompletedEventArgs* args) -> HRESULT {
141- std::wstring message = NavigationCompletedArgsToJsonString(
142- m_webviewEventSource.get(), args,
143- L"CoreWebView2Frame::NavigationCompleted");
144- PostEventMessage(message);
145- return S_OK;
146- })
147- .Get(),
148- NULL);
149- }
150- }
151-
152- void ScenarioWebViewEventMonitor::PostFrameCreatedEventMessage(wil::com_ptr<ICoreWebView2Frame > webviewFrame) {
153- wil::unique_cotaskmem_string name;
154- CHECK_FAILURE(webviewFrame->get_Name(&name));
155- auto frame5 = webviewFrame.try_query<ICoreWebView2Frame5 >();
156- if (frame5)
157- {
158- UINT32 frameId = 0;
159- CHECK_FAILURE(frame5->get_FrameId(&frameId));
160- }
161-
162- std::wstring message =
163- L"{ \"kind\": \"event\", \"name\": \"FrameCreated\", \"args\": {";
164-
165- message += L"\"frame\": " + EncodeQuote(name.get());
166- message += L",\"webview frame Id\": " + std::to_wstring((int)frameId) + L"}";
167- message +=
168- WebViewPropertiesToJsonString(m_webview.get());
169- message += L"}";
170- PostEventMessage(message);
87+ nullptr);
17188}
17289```
17390### C# Sample
17491``` c#
175- // Track first-level webview frame created event.
176- void ChildFrameEventsExecuted(object target, ExecutedRoutedEventArgs e)
177- {
178- webView.CoreWebView2.FrameCreated += HandleChildFrameCreated;
179- }
180-
181- // Track the creation, destruction, and navigation events of webview frames.
182- void HandleChildFrameCreated(object sender, CoreWebView2FrameCreatedEventArgs args)
183- {
184- CoreWebView2Frame childFrame = args.Frame;
185- string name = String.IsNullOrEmpty(childFrame.Name) ? "none" : childFrame.Name;
186- MessageBox.Show(this, "Id: " + childFrame.FrameId + " name: " + name, "Child frame created", MessageBoxButton.OK);
187- // Make a recursive call to track all nested webview frames events.
188- childFrame.FrameCreated += HandleChildFrameCreated;
189- childFrame.NavigationStarting += HandleChildFrameNavigationStarting;
190- childFrame.ContentLoading += HandleChildFrameContentLoading;
191- childFrame.DOMContentLoaded += HandleChildFrameDOMContentLoaded;
192- childFrame.NavigationCompleted += HandleChildFrameNavigationCompleted;
193- childFrame.Destroyed += HandleChildFrameDestroyed;
194- }
195-
196- void HandleChildFrameDestroyed(object sender, object args) {
197- CoreWebView2Frame frame = (CoreWebView2Frame)sender;
198- MessageBox.Show(this, "Id: " + frame.FrameId + " FrameDestroyed", "Child frame Destroyed", MessageBoxButton.OK);
199- }
200-
201- void HandleChildFrameNavigationStarting(object sender, CoreWebView2NavigationStartingEventArgs args)
202- {
203- CoreWebView2Frame frame = (CoreWebView2Frame)sender;
204- MessageBox.Show(this, "Id: " + frame.FrameId + " NavigationStarting", "Child frame Navigation", MessageBoxButton.OK);
205- }
206-
207- void HandleChildFrameContentLoading(object sender, CoreWebView2ContentLoadingEventArgs args)
208- {
209- CoreWebView2Frame frame = (CoreWebView2Frame)sender;
210- MessageBox.Show(this, "Id: " + frame.FrameId + " ContentLoading", "Child frame Content Loading", MessageBoxButton.OK);
211- }
212-
213- void HandleChildFrameDOMContentLoaded(object sender, CoreWebView2DOMContentLoadedEventArgs args)
214- {
215- CoreWebView2Frame frame = (CoreWebView2Frame)sender;
216- MessageBox.Show(this, "Id: " + frame.FrameId + " DOMContentLoaded", "Child frame DOM Content Loaded", MessageBoxButton.OK);
92+ var _frameNavigationUrls = new Dictionary <UINT32 , List <string >>();
93+ // In this example, a WebView2 application wants to manage the
94+ // navigation of third-party content residing in second-level iframes
95+ // (Main frame -> First-level frame -> second-level third-party frames).
96+ void RecordThirdPartyFrameNavigation () {
97+ webView .CoreWebView2 .FrameCreated += (sender , args ) =>
98+ {
99+ // Track nested (second-level) webview frame.
100+ args .Frame .FrameCreated += (frameCreatedSender , frameCreatedArgs ) =>
101+ {
102+ CoreWebView2Frame childFrame = frameCreatedArgs .Frame ;
103+ childFrame .NavigationStarting += HandleChildFrameNavigationStarting ;
104+ }
105+ }
217106}
218107
219- void HandleChildFrameNavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs args)
108+ void HandleChildFrameNavigationStarting (object sender ,
109+ CoreWebView2NavigationStartingEventArgs args )
220110{
111+ // Manage the navigation, e.g. cancel the navigation
112+ // if it's on block list.
221113 CoreWebView2Frame frame = (CoreWebView2Frame )sender ;
222- MessageBox.Show(this, "Id: " + frame.FrameId + " NavigationCompleted", "Child frame Navigation Completed", MessageBoxButton.OK);
114+ if (! _frameNavigationUrls .ContainsKey (frame .FrameId ))
115+ {
116+ _frameNavigationUrls [frame .FrameId ] = new List <string >();
117+ }
118+ // Log the navigation history per frame Id.
119+ _frameNavigationUrls [frame .FrameId ].Add (args .Uri );
223120}
224121```
225122
226123# API Details
227124## C++
228- ```
125+ ``` C++
229126// / Receives `FrameCreated` events.
230- interface ICoreWebView2FrameNestedFrameCreatedEventHandler : IUnknown {
127+ interface ICoreWebView2FrameChildFrameCreatedEventHandler : IUnknown {
231128 /// Provides the event args for the corresponding event.
232129 HRESULT Invoke(
233130 [ in] ICoreWebView2Frame* sender,
@@ -238,13 +135,13 @@ interface ICoreWebView2FrameNestedFrameCreatedEventHandler : IUnknown {
238135interface ICoreWebView2Frame7 : IUnknown {
239136 /// Adds an event handler for the ` FrameCreated ` event.
240137 /// Raised when a new direct descendant iframe is created.
241- /// Handle this event to get access to ICoreWebView2Frame objects.
242- /// Use `ICoreWebView2Frame. add_Destroyed` to listen for when this
138+ /// Handle this event to get access to ` ICoreWebView2Frame ` objects.
139+ /// Use ` ICoreWebView2Frame:: add_Destroyed ` to listen for when this
243140 /// iframe goes away.
244141 ///
245142 /// \snippet ScenarioWebViewEventMonitor.cpp AddFrameCreated
246143 HRESULT add_FrameCreated(
247- [in] ICoreWebView2FrameNestedFrameCreatedEventHandler * eventHandler,
144+ [ in] ICoreWebView2FrameChildFrameCreatedEventHandler * eventHandler,
248145 [ out] EventRegistrationToken* token);
249146
250147 /// Removes an event handler previously added with ` add_FrameCreated ` .
@@ -253,7 +150,7 @@ interface ICoreWebView2Frame7 : IUnknown {
253150}
254151```
255152
256- C#
153+ ## C#
257154``` c#
258155namespace Microsoft .Web .WebView2 .Core
259156{
@@ -266,3 +163,45 @@ namespace Microsoft.Web.WebView2.Core
266163 }
267164}
268165```
166+
167+ # Appendix
168+ ## Impacted API
169+ ### ` CoreWebView2Frame.PermissionRequested ` and ` CoreWebView2Frame.ScreenCaptureStarting `
170+ In the current case of nested iframes, the [ PermissionRequested] ( https://learn.microsoft.com/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2frame#permissionrequested )
171+ and [ ScreenCaptureStarting] ( https://learn.microsoft.com/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2frame#screencapturestarting )
172+ events will be raised from the top-level iframe. With the support
173+ of tracking nested iframes, this request can be handled directly
174+ by the nested iframe. Therefore, we now raise these requests to
175+ the nearest tracked frame, which is the ` CoreWebView2Frame ` closest
176+ to the frame that initiates the request (from bottom to top).
177+ ```
178+ // Example:
179+ // A (main frame/CoreWebView2)
180+ // |
181+ // B (first-level iframe/CoreWebView2Frame)
182+ // |
183+ // C (nested iframe)
184+ // |
185+ // D (nested iframe)
186+ ```
187+ Suppose there's a ` PermissionRequest ` comes from D.
188+ * If D is a tracked frame (` CoreWebView2Frame ` ), then D is the
189+ closet tracked frame from which the request will be raised from.
190+ * If D is not being tracked, and C is a tracked frame. Then C
191+ is the closet tracked frame from which the request will be
192+ raised from.
193+ * If neither C nor D is tracked, then B is the closet tracked
194+ frame from which the request will be raised. This case applies
195+ to current ` PermissionRequested ` developers, as they haven't
196+ subscribe to the ` CoreWebView2Frame.FrameCreated ` event.
197+ Therefore, requests originating from iframes will still be
198+ raised from the first-level iframe.
199+
200+ ### ` CoreWebView2.ProcessFailed `
201+ With the support of tracking nested iframes, the processes
202+ which support these nested iframes will be also tracked by
203+ [ ProcessFailed] ( https://learn.microsoft.com/dotnet/api/microsoft.web.webview2.core.corewebview2.processfailed )
204+ As we only track processes running tracked iframes, existing
205+ developers will not receive any process failed events specific
206+ to nested iframes as they haven't subscribe to the
207+ ` CoreWebView2Frame.FrameCreated ` event.
0 commit comments