Skip to content

Commit

Permalink
addressed comments from API review meeting
Browse files Browse the repository at this point in the history
  • Loading branch information
Wangsong Jin committed Dec 13, 2024
1 parent 2e237f6 commit ecb66d5
Showing 1 changed file with 197 additions and 62 deletions.
259 changes: 197 additions & 62 deletions specs/NestedFrame.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,99 +13,118 @@ giving them access to all properties, methods, and events of
[CoreWebView2Frame](https://learn.microsoft.com/dotnet/api/microsoft.web.webview2.core.corewebview2frame)
for the nested iframe.

To prevent unnecessary performance implication, WebView2 does
not track any nested iframes by default. It only tracks a nested
iframe if its parent iframe (`CoreWebView2Frame`) has subscribed
to the `CoreWebView2Frame.FrameCreated` API. For a page with
multi-level iframes, developers can choose to track only the
main page and first-level iframes (the default behavior), a
partial WebView2 frames tree with specific iframes of interest,
or the full WebView2 frames tree.
With the new API, developers can manage iframe tracking on a page
contains multiple levels of iframes. They can choose track only the
main page and first-level iframes (the default behavior), a partial
WebView2 frames tree with specific iframes of interest, or the full
WebView2 frames tree.

# Examples
## Track partial WebView2 Frames Tree
### C++ Sample
```cpp
```cpp
wil::com_ptr<ICoreWebView2> m_webview;
std::map<int, std::vector<std::wstring>> m_frame_navigation_urls;
// In this example, a WebView2 application wants to manage the
// navigation of third-party content residing in second-level iframes
std::map<UINT32, std::vector<std::wstring>> m_frame_navigation_urls;
// In this example, we present a scenario where a WebView2 application wants to
// manage the navigation of third-party content residing in second-level iframes
// (Main frame -> First-level frame -> Second-level third-party frames).
HRESULT RecordThirdPartyFrameNavigation() {
void TrackThirdPartyFrameNavigations()
{
auto webview2_4 = m_webView.try_query<ICoreWebView2_4>();
// Track the first-level webview frame.
webview2_4->add_FrameCreated(
if (webview2_4)
{
webview2_4->add_FrameCreated(
Callback<ICoreWebView2FrameCreatedEventHandler>(
[this](ICoreWebView2* sender, ICoreWebView2FrameCreatedEventArgs* args)
-> HRESULT {
[this](ICoreWebView2* sender, ICoreWebView2FrameCreatedEventArgs* args)
noexcept -> HRESULT
{
// [AddFrameCreated]
wil::com_ptr<ICoreWebView2Frame> webviewFrame;
CHECK_FAILURE(args->get_Frame(&webviewFrame));

// Track nested (second-level) webview frame.
auto frame7 = webviewFrame.try_query<ICoreWebView2Frame7>();
frame7->add_FrameCreated(
Callback<ICoreWebView2FrameChildFrameCreatedEventHandler>(
[this](
ICoreWebView2Frame* sender,
ICoreWebView2FrameCreatedEventArgs* args) -> HRESULT
{
wil::com_ptr<ICoreWebView2Frame> webviewFrame;
CHECK_FAILURE(args->get_Frame(&webviewFrame));
wil::com_ptr<ICoreWebView2Frame2> frame2 =
webviewFrame.try_query<ICoreWebView2Frame2>();
if (frame2)
if (frame7)
{
frame7->add_FrameCreated(
Callback<ICoreWebView2FrameChildFrameCreatedEventHandler>(
[this](
ICoreWebView2Frame* sender,
ICoreWebView2FrameCreatedEventArgs* args) noexcept
-> HRESULT
{
// Subscribe to nested (second-level) webview frame navigation
// starting event.
frame2->add_NavigationStarting(
Callback<ICoreWebView2FrameNavigationStartingEventHandler>(
[this](
ICoreWebView2Frame* sender,
ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT {
// Manage the navigation, e.g. cancel the
// navigation if it's on block list.
UINT32 frameId = 0;
auto frame5 = wil::com_ptr<ICoreWebView2Frame>(sender)
.try_query<ICoreWebView2Frame5>();
CHECK_FAILURE(frame5->get_FrameId(&frameId));
wil::unique_cotaskmem_string uri;
CHECK_FAILURE(args->get_Uri(&uri));
// Log the navigation history per frame Id.
m_frame_navigation_urls[(int)frameId].push_back(uri.get());
return S_OK;
})
.Get(),
nullptr);
}
return S_OK;
})
.Get(),
nullptr);
wil::com_ptr<ICoreWebView2Frame> webviewFrame;
CHECK_FAILURE(args->get_Frame(&webviewFrame));

wil::com_ptr<ICoreWebView2Frame2> frame2 =
webviewFrame.try_query<ICoreWebView2Frame2>();
if (frame2)
{
// Subscribe to nested (second-level) webview frame
// navigation starting event.
frame2->add_NavigationStarting(
Callback<
ICoreWebView2FrameNavigationStartingEventHandler>(
[this](
ICoreWebView2Frame* sender,
ICoreWebView2NavigationStartingEventArgs* args)
noexcept -> HRESULT
{
// Manage the navigation, e.g. cancel the
// navigation if it's on block list.

UINT32 frameId = 0;
auto frame5 =
wil::try_com_query<ICoreWebView2Frame5>(sender);
if (frame5)
{
CHECK_FAILURE(
frame5->get_FrameId(&frameId));
}
wil::unique_cotaskmem_string uri;
CHECK_FAILURE(args->get_Uri(&uri));

// Log the navigation history per frame Id.
m_frame_navigation_urls[frameId].push_back(uri.get());
return S_OK;
})
.Get(),
nullptr);
}
return S_OK;
})
.Get(),
nullptr);
}
// [AddFrameCreated]
return S_OK;
})
.Get(),
nullptr);
nullptr);
}
}

```
### C# Sample
```c#
var _frameNavigationUrls = new Dictionary<UINT32, List<string>>();
// In this example, a WebView2 application wants to manage the
// navigation of third-party content residing in second-level iframes
// In this example, we present a scenario where a WebView2 application wants to
// manage the navigation of third-party content residing in second-level iframes
// (Main frame -> First-level frame -> second-level third-party frames).
void RecordThirdPartyFrameNavigation() {
void TrackThirdPartyFrameNavigations()
{
webView.CoreWebView2.FrameCreated += (sender, args) =>
{
// Track nested (second-level) webview frame.
args.Frame.FrameCreated += (frameCreatedSender, frameCreatedArgs) =>
{
CoreWebView2Frame childFrame = frameCreatedArgs.Frame;
childFrame.NavigationStarting += HandleChildFrameNavigationStarting;
}
}
childFrame.NavigationStarting += OnFrameNavigationStarting;
};
};
}

void HandleChildFrameNavigationStarting(object sender,
void OnFrameNavigationStarting(object sender,
CoreWebView2NavigationStartingEventArgs args)
{
// Manage the navigation, e.g. cancel the navigation
Expand All @@ -120,6 +139,122 @@ void HandleChildFrameNavigationStarting(object sender,
}
```

## Track entire WebView2 Frames Tree
### C++ Sample
```C++
std::map<UINT32, std::vector<std::wstring>> m_frame_navigation_urls;
void OnFrameCreated(wil::com_ptr<ICoreWebView2Frame> webviewFrame);
// In this example, we present a scenario where a WebView2 application
// wants to manage the navigation in all iframes.
void TrackAllFrameNavigations(wil::com_ptr<ICoreWebView2> webview)
{
auto webview2_4 = webview.try_query<ICoreWebView2_4>();
if (webview2_4)
{
webview2_4->add_FrameCreated(
Callback<ICoreWebView2FrameCreatedEventHandler>(
[this](ICoreWebView2* sender, ICoreWebView2FrameCreatedEventArgs* args)
noexcept -> HRESULT
{
wil::com_ptr<ICoreWebView2Frame> webviewFrame;
CHECK_FAILURE(args->get_Frame(&webviewFrame));
// Track first-level webview frame.
OnFrameCreated(webviewFrame);
return S_OK;
})
.Get(),
nullptr);
}
}

void OnFrameCreated(wil::com_ptr<ICoreWebView2Frame> webviewFrame)
{
auto frame7 = webviewFrame.try_query<ICoreWebView2Frame7>();
if (frame7)
{
//! [AddFrameCreated]
frame7->add_FrameCreated(
Callback<ICoreWebView2FrameNestedFrameCreatedEventHandler>(
[this](
ICoreWebView2Frame* sender,
ICoreWebView2FrameCreatedEventArgs* args) noexcept -> HRESULT
{
wil::com_ptr<ICoreWebView2Frame> webviewFrame;
CHECK_FAILURE(args->get_Frame(&webviewFrame));
// Make a recursive call to track all nested
// webview frame.
OnFrameCreated(webviewFrame);
return S_OK;
})
.Get(),
nullptr);
//! [AddFrameCreated]
}

// Subscribe to webview frame navigation starting event.
wil::com_ptr<ICoreWebView2Frame2> frame2 = webviewFrame.try_query<ICoreWebView2Frame2>();
if (frame2)
{
frame2->add_NavigationStarting(
Callback<ICoreWebView2FrameNavigationStartingEventHandler>(
[this](
ICoreWebView2Frame* sender,
ICoreWebView2NavigationStartingEventArgs* args) noexcept -> HRESULT
{
// Manage the navigation, e.g. cancel the
// navigation if it's on block list.

UINT32 frameId = 0;
auto frame5 = wil::try_com_query<ICoreWebView2Frame5>(sender);
if (frame5)
{
CHECK_FAILURE(frame5->get_FrameId(&frameId));
}
wil::unique_cotaskmem_string uri;
CHECK_FAILURE(args->get_Uri(&uri));

// Log the navigation history per frame Id.
m_frame_navigation_urls[frameId].push_back(uri.get());
return S_OK;
})
.Get(),
nullptr);
}
}
```
### C# Sample
```C#
var _frameNavigationUrls = new Dictionary<UINT32, List<string>>();
// In this example, we present a scenario where a WebView2 application
// wants to manage the navigation in all iframes.
void TrackAllFrameNavigations(object target, ExecutedRoutedEventArgs e)
{
webView.CoreWebView2.FrameCreated += OnFrameCreated;
}
void OnFrameCreated(object sender, CoreWebView2FrameCreatedEventArgs args)
{
CoreWebView2Frame childFrame = args.Frame;
// Make a recursive call to track all nested webview frames event.
childFrame.FrameCreated += OnFrameCreated;
childFrame.NavigationStarting += OnFrameNavigationStarting;
}
void OnFrameNavigationStarting(object sender, CoreWebView2NavigationStartingEventArgs args)
{
// Manage the navigation, e.g. cancel the navigation
// if it's on block list.
CoreWebView2Frame frame = (CoreWebView2Frame)sender;
if (!_frameNavigationUrls.ContainsKey(frame.FrameId))
{
_frameNavigationUrls[frame.FrameId] = new List<string>();
}
// Log the navigation history per frame Id.
_frameNavigationUrls[frame.FrameId].Add(args.Uri);
}
```

# API Details
## C++
```C++
Expand Down

0 comments on commit ecb66d5

Please sign in to comment.