-
Notifications
You must be signed in to change notification settings - Fork 54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
API Review: CoreWebView2Frame.FrameCreated #4982
base: main
Are you sure you want to change the base?
Conversation
Add spec for NestedFrame.md
specs/NestedFrame.md
Outdated
for the nested iframe. | ||
|
||
To prevent unnecessary performance implication, WebView2 does | ||
not track any nested iframes by default. It only tracks a nested |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to violate Windows Runtime guidelines, because it means that you don't get a CoreWebView2.FrameNavigationStarting unless you subscribe to the FrameCreated event (even if the event handler does nothing).
❌ DO NOT alter behavior based on whether an event handler is registered or the number of registered event handlers. In other words, registering an event handler that does nothing should have no effect on behavior.
An implementation is permitted to perform optimizations based on whether an event handler is registered. The most common case of this is bypassing an expensive operation if there is no way for the app to observe any of the operation’s side effects.
In this case, the app can observe the side effects (through the FrameNavigationStarting event).
We could have a new Boolean property on the frame ShouldRaiseNavigationEvents
which defaults to true for first-level frames and false for deeper frames. (But see the componentization problem later.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to clarify the documentation here. The CoreWebView2.FrameNavigationStarting
event will still be triggered for all child iframes, regardless of whether we subscribe to the CoreWebView2Frame.FrameCreated
event or not. In this context, we are referring to the APIs in CoreWebView2Frame. We think that event handler matters because if developers do not subscribe to it, there is no need to expose any CoreWebView2Frame APIs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please clarify that this is an implementation note. This is only about an internal perf optimization - no observable behavior changes described here.
specs/NestedFrame.md
Outdated
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} | |
} | |
}; | |
}; | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please fix.
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 | ||
// (Main frame -> First-level frame -> Second-level third-party frames). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe also a sample showing full depth frame tracking?
void TrackAllFrameNavigations()
{
webView.CoreWebView2.FrameCreated += (_, args) =>
{
OnFrameCreated(args.Frame);
};
}
void OnFrameCreated(CoreWebView2Frame frame)
{
frame.FrameCreated += (_, e) => OnFrameCreated(e.Frame);
frame.NavigationStarting += OnFrameNavigationStarting;
}
void OnFrameNavigationStarting(object sender, CoreWebView2NavigationStartingEventArgs e)
{
// as before
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please consider adding an additional sample.
specs/NestedFrame.md
Outdated
CHECK_FAILURE(args->get_Frame(&webviewFrame)); | ||
// Track nested (second-level) webview frame. | ||
auto frame7 = webviewFrame.try_query<ICoreWebView2Frame7>(); | ||
frame7->add_FrameCreated( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need a null check on frame7
in case the try_query
fails.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please fix.
specs/NestedFrame.md
Outdated
Callback<ICoreWebView2FrameNavigationStartingEventHandler>( | ||
[this]( | ||
ICoreWebView2Frame* sender, | ||
ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT { | |
ICoreWebView2NavigationStartingEventArgs* args) noexcept -> HRESULT { |
Fail fast if an exception comes out of the []
or push_back
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please fix.
specs/NestedFrame.md
Outdated
### C++ Sample | ||
```cpp | ||
wil::com_ptr<ICoreWebView2> m_webview; | ||
std::map<int, std::vector<std::wstring>> m_frame_navigation_urls; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::map<int, std::vector<std::wstring>> m_frame_navigation_urls; | |
std::map<UINT32, std::vector<std::wstring>> m_frame_navigation_urls; |
Why not just use a UINT32
to match the type of FrameId
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please fix.
specs/NestedFrame.md
Outdated
// navigation if it's on block list. | ||
UINT32 frameId = 0; | ||
auto frame5 = wil::com_ptr<ICoreWebView2Frame>(sender) | ||
.try_query<ICoreWebView2Frame5>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing null check from try_query. Maybe we should use ICoreWebView2Frame5 in the outer lambda, so that we don't even get here if v5 is not supported. Then that would allow us to use query() instead of try_query().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please consider simple fix of null check for simple sample code.
specs/NestedFrame.md
Outdated
// navigation if it's on block list. | ||
UINT32 frameId = 0; | ||
auto frame5 = wil::com_ptr<ICoreWebView2Frame>(sender) | ||
.try_query<ICoreWebView2Frame5>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be simplified to
auto frame5 = wil::try_com_query<ICoreWebView2Frame5>(sender);
avoids an unnecessary temporary com_ptr.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please fix.
events will be raised from the top-level iframe. With the support | ||
of tracking nested iframes, we can now handle these requests directly | ||
within the nested iframe. Specifically, these requests are raised to | ||
the nearest tracked frame, which is the `CoreWebView2Frame` closest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could lead to a problem if an app combines multiple components, and one of them adds a FrameCreated handler and accidentally messes up the other component which expected the event from the top-level frame. Is the answer "Yeah, you can't really componentize your app like that because features used by one app might have adverse consequences far away." So it means I can't have a "app usage telemetry" component that hooks all frame activity and logs it, because that would accidentally break my "permission manager" component.
From the Windows Runtime design guidelines:
A Well-Designed API Avoids Shared Behavior
A developer is often unpleasantly surprised when he or she uses one API that causes a side-effect elsewhere.
☑ DO design APIs to allow multiple components running in the same process to use the API without having to explicitly coordinate, for example, two plug-ins loaded into the same instance of Internet Explorer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For PermissionRequested
API, there are two factors. One is the presence of the FrameCreated
handler, and the other is the need to handle the PermissionRequested
API. Simply subscribing to the FrameCreated
handler does not affect where the PermissionRequested
event is actually handled.
Suppose there is a permission request from frame D. Developers want to handle the PermissionRequested
event from the top-level iframe B in component I. Meanwhile, in component II, developers have an "app usage telemetry" that hooks all frame activity. Unless the developers subscribe to the permission request and Handled in either frame D or frame C, otherwise the PermissionRequested
event will still bubble up to the top-level iframe and handle in component I.
// Example:
// A (main frame/CoreWebView2)
// |
// B (first-level iframe/CoreWebView2Frame)
// |
// C (nested iframe)
// |
// D (nested iframe)
But I think this kind of shared behavior should be avoid by developers. Even if we, align with the browser, track all iframes by default and the PermissionRequested
event starts to fire on the iframe where it originates, there could be shared behavior issue. If developers handle the PermissionRequested
event in a nested iframe within one component, then another component won't have the opportunity to handle the PermissionRequested
event at the top-level iframe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No issue after discussion: event bubbles out in existing event bubbling pattern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please update according to comments, thanks!
specs/NestedFrame.md
Outdated
for the nested iframe. | ||
|
||
To prevent unnecessary performance implication, WebView2 does | ||
not track any nested iframes by default. It only tracks a nested |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please clarify that this is an implementation note. This is only about an internal perf optimization - no observable behavior changes described here.
specs/NestedFrame.md
Outdated
### C++ Sample | ||
```cpp | ||
wil::com_ptr<ICoreWebView2> m_webview; | ||
std::map<int, std::vector<std::wstring>> m_frame_navigation_urls; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please fix.
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 | ||
// (Main frame -> First-level frame -> Second-level third-party frames). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please consider adding an additional sample.
specs/NestedFrame.md
Outdated
CHECK_FAILURE(args->get_Frame(&webviewFrame)); | ||
// Track nested (second-level) webview frame. | ||
auto frame7 = webviewFrame.try_query<ICoreWebView2Frame7>(); | ||
frame7->add_FrameCreated( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please fix.
specs/NestedFrame.md
Outdated
Callback<ICoreWebView2FrameNavigationStartingEventHandler>( | ||
[this]( | ||
ICoreWebView2Frame* sender, | ||
ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please fix.
specs/NestedFrame.md
Outdated
// navigation if it's on block list. | ||
UINT32 frameId = 0; | ||
auto frame5 = wil::com_ptr<ICoreWebView2Frame>(sender) | ||
.try_query<ICoreWebView2Frame5>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please consider simple fix of null check for simple sample code.
specs/NestedFrame.md
Outdated
// navigation if it's on block list. | ||
UINT32 frameId = 0; | ||
auto frame5 = wil::com_ptr<ICoreWebView2Frame>(sender) | ||
.try_query<ICoreWebView2Frame5>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please fix.
specs/NestedFrame.md
Outdated
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please fix.
events will be raised from the top-level iframe. With the support | ||
of tracking nested iframes, we can now handle these requests directly | ||
within the nested iframe. Specifically, these requests are raised to | ||
the nearest tracked frame, which is the `CoreWebView2Frame` closest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No issue after discussion: event bubbles out in existing event bubbling pattern.
ecb66d5
to
7e5018c
Compare
7e5018c
to
4d00028
Compare
This is a review for the new
CoreWebView2Frame.FrameCreated
API.