@@ -5,8 +5,8 @@ CoreWebView2Frame.FrameCreated API
5
5
At present, WebView2 enables developers to track only first-level
6
6
iframes, which are the direct child iframes of the main frame.
7
7
However, 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
10
10
` CoreWebView2Frame.FrameCreated ` API. This new API will allow
11
11
developers to subscribe to the nested iframe creation event,
12
12
giving them access to all properties, methods, and events of
@@ -15,7 +15,7 @@ for the nested iframe.
15
15
16
16
To prevent unnecessary performance implication, WebView2 does
17
17
not 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
19
19
to the ` CoreWebView2Frame.FrameCreated ` API. For a page with
20
20
multi-level iframes, developers can choose to track only the
21
21
main page and first-level iframes (the default behavior), a
@@ -25,209 +25,106 @@ or the full WebView2 frames tree.
25
25
# Examples
26
26
### C++ Sample
27
27
``` 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(
35
37
Callback<ICoreWebView2FrameCreatedEventHandler>(
36
38
[this](ICoreWebView2* sender, ICoreWebView2FrameCreatedEventArgs* args)
37
39
-> HRESULT {
40
+ // [AddFrameCreated]
38
41
wil::com_ptr<ICoreWebView2Frame> webviewFrame;
39
42
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]
43
84
return S_OK;
44
85
})
45
86
.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);
171
88
}
172
89
```
173
90
### C# Sample
174
91
``` 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
+ }
217
106
}
218
107
219
- void HandleChildFrameNavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs args)
108
+ void HandleChildFrameNavigationStarting (object sender ,
109
+ CoreWebView2NavigationStartingEventArgs args )
220
110
{
111
+ // Manage the navigation, e.g. cancel the navigation
112
+ // if it's on block list.
221
113
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 );
223
120
}
224
121
```
225
122
226
123
# API Details
227
124
## C++
228
- ```
125
+ ``` C++
229
126
// / Receives `FrameCreated` events.
230
- interface ICoreWebView2FrameNestedFrameCreatedEventHandler : IUnknown {
127
+ interface ICoreWebView2FrameChildFrameCreatedEventHandler : IUnknown {
231
128
/// Provides the event args for the corresponding event.
232
129
HRESULT Invoke(
233
130
[ in] ICoreWebView2Frame* sender,
@@ -238,13 +135,13 @@ interface ICoreWebView2FrameNestedFrameCreatedEventHandler : IUnknown {
238
135
interface ICoreWebView2Frame7 : IUnknown {
239
136
/// Adds an event handler for the ` FrameCreated ` event.
240
137
/// 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
243
140
/// iframe goes away.
244
141
///
245
142
/// \snippet ScenarioWebViewEventMonitor.cpp AddFrameCreated
246
143
HRESULT add_FrameCreated(
247
- [in] ICoreWebView2FrameNestedFrameCreatedEventHandler * eventHandler,
144
+ [ in] ICoreWebView2FrameChildFrameCreatedEventHandler * eventHandler,
248
145
[ out] EventRegistrationToken* token);
249
146
250
147
/// Removes an event handler previously added with ` add_FrameCreated ` .
@@ -253,7 +150,7 @@ interface ICoreWebView2Frame7 : IUnknown {
253
150
}
254
151
```
255
152
256
- C#
153
+ ## C#
257
154
``` c#
258
155
namespace Microsoft .Web .WebView2 .Core
259
156
{
@@ -266,3 +163,45 @@ namespace Microsoft.Web.WebView2.Core
266
163
}
267
164
}
268
165
```
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