Skip to content

Commit 2e237f6

Browse files
Merge pull request #4950 from MicrosoftEdge/NestedFrame-draft
Add spec for NestedFrame.md
2 parents 47b5622 + 04b1b94 commit 2e237f6

File tree

1 file changed

+215
-0
lines changed

1 file changed

+215
-0
lines changed

specs/NestedFrame.md

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
CoreWebView2Frame.FrameCreated API
2+
===
3+
4+
# Background
5+
At present, WebView2 enables developers to track only first-level
6+
iframes, which are the direct child iframes of the main frame.
7+
However, we see that WebView2 customers want to manage nested
8+
iframes, such as recording the navigation history for a second
9+
level iframe. To address this, we will introduce the
10+
`CoreWebView2Frame.FrameCreated` API. This new API will allow
11+
developers to subscribe to the nested iframe creation event,
12+
giving them access to all properties, methods, and events of
13+
[CoreWebView2Frame](https://learn.microsoft.com/dotnet/api/microsoft.web.webview2.core.corewebview2frame)
14+
for the nested iframe.
15+
16+
To prevent unnecessary performance implication, WebView2 does
17+
not track any nested iframes by default. It only tracks a nested
18+
iframe if its parent iframe (`CoreWebView2Frame`) has subscribed
19+
to the `CoreWebView2Frame.FrameCreated` API. For a page with
20+
multi-level iframes, developers can choose to track only the
21+
main page and first-level iframes (the default behavior), a
22+
partial WebView2 frames tree with specific iframes of interest,
23+
or the full WebView2 frames tree.
24+
25+
# Examples
26+
### C++ Sample
27+
```cpp
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(
37+
Callback<ICoreWebView2FrameCreatedEventHandler>(
38+
[this](ICoreWebView2* sender, ICoreWebView2FrameCreatedEventArgs* args)
39+
-> HRESULT {
40+
// [AddFrameCreated]
41+
wil::com_ptr<ICoreWebView2Frame> webviewFrame;
42+
CHECK_FAILURE(args->get_Frame(&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]
84+
return S_OK;
85+
})
86+
.Get(),
87+
nullptr);
88+
}
89+
```
90+
### C# Sample
91+
```c#
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+
}
106+
}
107+
108+
void HandleChildFrameNavigationStarting(object sender,
109+
CoreWebView2NavigationStartingEventArgs args)
110+
{
111+
// Manage the navigation, e.g. cancel the navigation
112+
// if it's on block list.
113+
CoreWebView2Frame frame = (CoreWebView2Frame)sender;
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);
120+
}
121+
```
122+
123+
# API Details
124+
## C++
125+
```C++
126+
/// Receives `FrameCreated` events.
127+
interface ICoreWebView2FrameChildFrameCreatedEventHandler : IUnknown {
128+
/// Provides the event args for the corresponding event.
129+
HRESULT Invoke(
130+
[in] ICoreWebView2Frame* sender,
131+
[in] ICoreWebView2FrameCreatedEventArgs* args);
132+
}
133+
134+
/// This is the ICoreWebView2Frame interface.
135+
interface ICoreWebView2Frame7 : IUnknown {
136+
/// Adds an event handler for the `FrameCreated` event.
137+
/// Raised when a new direct descendant iframe is created.
138+
/// Handle this event to get access to `ICoreWebView2Frame` objects.
139+
/// Use `ICoreWebView2Frame::add_Destroyed` to listen for when this
140+
/// iframe goes away.
141+
///
142+
/// \snippet ScenarioWebViewEventMonitor.cpp AddFrameCreated
143+
HRESULT add_FrameCreated(
144+
[in] ICoreWebView2FrameChildFrameCreatedEventHandler* eventHandler,
145+
[out] EventRegistrationToken* token);
146+
147+
/// Removes an event handler previously added with `add_FrameCreated`.
148+
HRESULT remove_FrameCreated(
149+
[in] EventRegistrationToken token);
150+
}
151+
```
152+
153+
## C#
154+
```c#
155+
namespace Microsoft.Web.WebView2.Core
156+
{
157+
runtimeclass CoreWebView2Frame
158+
{
159+
[interface_name("Microsoft.Web.WebView2.Core.ICoreWebView2Frame7")]
160+
{
161+
event Windows.Foundation.TypedEventHandler<CoreWebView2Frame, CoreWebView2FrameCreatedEventArgs> FrameCreated;
162+
}
163+
}
164+
}
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, we can now handle these requests directly
174+
within the nested iframe. Specifically, these requests are raised 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+
If the `PermissionRequested` event is not handled in the current
201+
tracked frame, the request will propagate to its parent
202+
`CoreWebView2Frame`, or to `CoreWebView2` if the parent frame
203+
is the main frame. For example, if frame D is tracked but does
204+
not handle the request, the request will bubble up to frame C.
205+
If frame C handles the request, it will not propagate further
206+
to its parent frame B.
207+
208+
### `CoreWebView2.ProcessFailed`
209+
With the support of tracking nested iframes, the processes
210+
which support these nested iframes will be also tracked by
211+
[ProcessFailed](https://learn.microsoft.com/dotnet/api/microsoft.web.webview2.core.corewebview2.processfailed).
212+
As we only track processes running tracked iframes, existing
213+
developers will not receive any process failed events specific
214+
to nested iframes as they haven't subscribe to the
215+
`CoreWebView2Frame.FrameCreated` event.

0 commit comments

Comments
 (0)