Skip to content

Commit 6e42f58

Browse files
author
Wangsong Jin
committed
refined the sample code and added impact APIs description
1 parent 8d67f77 commit 6e42f58

File tree

1 file changed

+127
-188
lines changed

1 file changed

+127
-188
lines changed

specs/NestedFrame.md

Lines changed: 127 additions & 188 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ CoreWebView2Frame.FrameCreated API
55
At present, WebView2 enables developers to track only first-level
66
iframes, which are the direct child iframes of the main frame.
77
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
1010
`CoreWebView2Frame.FrameCreated` API. This new API will allow
1111
developers to subscribe to the nested iframe creation event,
1212
giving them access to all properties, methods, and events of
@@ -15,7 +15,7 @@ for the nested iframe.
1515

1616
To prevent unnecessary performance implication, WebView2 does
1717
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
1919
to the `CoreWebView2Frame.FrameCreated` API. For a page with
2020
multi-level iframes, developers can choose to track only the
2121
main 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 {
238135
interface 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#
258155
namespace 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

Comments
 (0)