Skip to content

API Review: Frame-Level LaunchingExternalUriScheme#5621

Open
vipulgupta11 wants to merge 5 commits into
user/vipulgupta11/FrameLaunchingExternalUriSchemefrom
user/vipulgupta11/FrameLaunchingExternalUriScheme-draft
Open

API Review: Frame-Level LaunchingExternalUriScheme#5621
vipulgupta11 wants to merge 5 commits into
user/vipulgupta11/FrameLaunchingExternalUriSchemefrom
user/vipulgupta11/FrameLaunchingExternalUriScheme-draft

Conversation

@vipulgupta11

Copy link
Copy Markdown

Add FrameLaunchingExternalUriScheme.md — proposes raising the LaunchingExternalUriScheme event on CoreWebView2Frame (in addition to the existing event on CoreWebView2) so host apps can attribute external-URI launches to a specific iframe via the event sender. Also proposes a Handled property on CoreWebView2LaunchingExternalUriSchemeEventArgs so per-iframe handlers can suppress the webview-level handler.

Same shape as CoreWebView2Frame.PermissionRequested + CoreWebView2PermissionRequestedEventArgs2.Handled.

Adds a frame-level LaunchingExternalUriScheme event on CoreWebView2Frame
and a Handled property on CoreWebView2LaunchingExternalUriSchemeEventArgs.
This lets host apps that embed multiple sub-applications as iframes
attribute external-URI launches to a specific iframe via the event
sender, and lets per-iframe handlers suppress the webview-level handler.

Mirrors the existing ICoreWebView2Frame3.add_PermissionRequested +
ICoreWebView2PermissionRequestedEventArgs2.Handled shape.
@vipulgupta11 vipulgupta11 added the API Proposal Review WebView2 API Proposal for review. label Jun 9, 2026
@vipulgupta11

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree company="Microsoft"

@vipulgupta11 vipulgupta11 marked this pull request as ready for review June 10, 2026 06:52
@vipulgupta11 vipulgupta11 requested a review from shrinaths June 10, 2026 06:52
@vipulgupta11 vipulgupta11 self-assigned this Jun 10, 2026
@@ -0,0 +1,401 @@
Frame-Level LaunchingExternalUriScheme

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requires rewriting to align with Windows Api documentation style. Voice, grammar, tone etc needs correction. Change to something like ...

Frame-Level LaunchingExternalUriScheme

Background

WebView2 raises LaunchingExternalUriScheme on CoreWebView2
when web content attempts to launch a URI scheme registered with the OS
as an external handler (for example, mailto:, tel:, or a custom protocol).
The host can intercept this event to suppress the default WebView2 dialog,
present custom consent UI, and set Cancel to control whether the URI is launched.

The event currently exposes Uri, InitiatingOrigin,
IsUserInitiated, and Cancel. It does not identify the originating
iframe. As a result, hosts that embed multiple sub-applications in
iframes within a single WebView2—often served from a shared origin—
cannot reliably attribute a launch to a specific iframe.

This limitation impacts several scenarios:

  1. Multiple iframes may share the same origin, and the event exposes no
    identifier beyond the origin.
  2. The same iframe content may be hosted in multiple surfaces (for example,
    windows or panels), and the host cannot route consent UI to the correct surface.
  3. Sandboxed and srcdoc iframes have opaque origins. The API reports
    the parent origin, making the initiating iframe indistinguishable.

To enable iframe-level attribution, LaunchingExternalUriScheme is also
raised on CoreWebView2Frame. This allows handlers to be registered per
iframe and provides direct attribution through the iframe instance as
the event sender.

CoreWebView2LaunchingExternalUriSchemeEventArgs includes a Handled
property that allows a frame-level handler to prevent the event from
being raised on CoreWebView2.

This design aligns with the per-frame event model used by:

  • ICoreWebView2Frame3::add_PermissionRequested
  • ICoreWebView2PermissionRequestedEventArgs2::Handled

A related attribution mechanism exists in
ICoreWebView2NewWindowRequestedEventArgs3::OriginalSourceFrameInfo.
The differences between these approaches are described in the Appendix.


Description

CoreWebView2Frame exposes a LaunchingExternalUriScheme event.

The event is raised when content in a CoreWebView2Frame, or in a
descendant iframe that does not have a closer tracked
CoreWebView2Frame ancestor, attempts to launch an external URI scheme.
The event sender is the CoreWebView2Frame, enabling direct attribution
to the initiating iframe.

CoreWebView2LaunchingExternalUriSchemeEventArgs includes a Handled
property.

By default, the event is raised on both CoreWebView2Frame and
CoreWebView2. Frame-level handlers are invoked before webview-level
handlers. If Handled is set to TRUE in a frame-level handler, the event
is not raised on CoreWebView2, and its handlers are not invoked.

For nested iframes, the event is raised on the closest tracked
CoreWebView2Frame in the ancestor chain. This matches the routing
behavior of CoreWebView2Frame.PermissionRequested.

Event args are shared between handler tiers. Properties set in the
frame-level handler, including Cancel and Handled, are visible to
webview-level handlers. A Deferral taken in either handler tier blocks
the launch until the deferral is completed.

If a deferral is taken, Handled must be set synchronously before taking
the deferral to prevent CoreWebView2-level handlers from being invoked.

Cancel controls whether the URI is launched. Handled controls whether
webview-level handlers are invoked.

Nested iframes

The event is raised on the closest tracked CoreWebView2Frame in the
ancestor chain. This matches existing routing behavior for frame-level
events.

Same-origin and cross-origin iframes

The event is raised regardless of origin. For cross-origin iframes
without a user gesture, existing WebView2 behavior applies: the launch
is blocked and the event is not raised, consistent with
CoreWebView2.LaunchingExternalUriScheme.

Examples

Registering a per-iframe handler

A host embedding multiple sub-applications in iframes can register a
handler per iframe to attribute external URI launches and present
iframe-specific consent UI. Setting Handled = TRUE prevents the
webview-level handler from being invoked.

C++

AppWindow* m_appWindow;
wil::com_ptr<ICoreWebView2> m_webview;
EventRegistrationToken m_frameCreatedToken = {};
EventRegistrationToken m_frameLaunchingExternalUriSchemeToken = {};

void RegisterFrameLaunchingExternalUriSchemeHandler()
{
    auto webview4 = m_webview.try_query<ICoreWebView2_4>();
    if (!webview4)
    {
        return;
    }

    CHECK_FAILURE(webview4->add_FrameCreated(
        Callback<ICoreWebView2FrameCreatedEventHandler>(
            [this](ICoreWebView2* sender,
                   ICoreWebView2FrameCreatedEventArgs* args) -> HRESULT
            {
                wil::com_ptr<ICoreWebView2Frame> webviewFrame;
                CHECK_FAILURE(args->get_Frame(&webviewFrame));

                auto frame10 = webviewFrame
                    .try_query<ICoreWebView2ExperimentalFrame10>();
                if (!frame10)
                {
                    return S_OK;
                }

                CHECK_FAILURE(frame10->add_LaunchingExternalUriScheme(
                    Callback<
                        ICoreWebView2ExperimentalFrameLaunchingExternalUriSchemeEventHandler>(
                        [this](
                            ICoreWebView2Frame* frameSender,
                            ICoreWebView2LaunchingExternalUriSchemeEventArgs2*
                                args) -> HRESULT
                        {
                            // Avoid reentrancy by scheduling the dialog
                            // asynchronously outside the event handler.
                            // Because a deferral is taken, set `Handled`
                            // to TRUE synchronously before taking the
                            // deferral. This prevents `CoreWebView2`-level
                            // handlers from being invoked.

                            CHECK_FAILURE(args->put_Handled(TRUE));

                            wil::com_ptr<ICoreWebView2Deferral> deferral;
                            CHECK_FAILURE(args->GetDeferral(&deferral));

                            wil::com_ptr<ICoreWebView2Frame> sender(frameSender);

                            m_appWindow->RunAsync(
                                [sender, deferral, args]
                                {
                                    wil::unique_cotaskmem_string frameName;
                                    CHECK_FAILURE(
                                        sender->get_Name(&frameName));

                                    wil::unique_cotaskmem_string uri;
                                    CHECK_FAILURE(args->get_Uri(&uri));

                                    std::wstring message = L"The \"";
                                    message += frameName.get();
                                    message += L"\" iframe is attempting to launch an external URI scheme for ";
                                    message += uri.get();
                                    message += L".\n\nAllow this action?";

                                    int response = MessageBox(
                                        nullptr, message.c_str(),
                                        L"Launching External URI Scheme",
                                        MB_YESNO | MB_ICONQUESTION);

                                    CHECK_FAILURE(args->put_Cancel(
                                        response == IDYES ? FALSE : TRUE));

                                    CHECK_FAILURE(deferral->Complete());
                                });

                            return S_OK;
                        })
                        .Get(),
                    &m_frameLaunchingExternalUriSchemeToken));

                return S_OK;
            })
            .Get(),
        &m_frameCreatedToken));
}

C#

private WebView2 m_webview;

void RegisterFrameLaunchingExternalUriSchemeHandler()
{
    m_webview.CoreWebView2.FrameCreated += (sender, frameCreatedArgs) =>
    {
        frameCreatedArgs.Frame.LaunchingExternalUriScheme +=
            async (frameSender, args) =>
        {
            // Because asynchronous work is awaited below, set `Handled`
            // synchronously before taking the deferral. This prevents
            // `CoreWebView2`-level handlers from being invoked.

            args.Handled = true;

            CoreWebView2Deferral deferral = args.GetDeferral();
            using (deferral)
            {
                string message =
                    $"The \"{frameSender.Name}\" iframe is " +
                    $"attempting to launch an external URI scheme for " +
                    $"{args.Uri}.\n\nAllow this action?";

                MessageBoxResult selection = MessageBox.Show(
                    message,
                    "Launching External URI Scheme",
                    MessageBoxButton.YesNo);

                args.Cancel = selection != MessageBoxResult.Yes;
            }
        };
    };
}

API Details

Win32 (C++)

interface ICoreWebView2ExperimentalFrame10;
interface ICoreWebView2ExperimentalFrameLaunchingExternalUriSchemeEventHandler;
interface ICoreWebView2LaunchingExternalUriSchemeEventArgs2;

/// Extends the `ICoreWebView2Frame` interface to expose the
/// `LaunchingExternalUriScheme` event at the iframe level.
/// Host applications can subscribe on a per-frame basis to attribute
/// external URI scheme launches to a specific iframe, even when multiple
/// frames share the same origin.
[uuid(0e3c31b7-bbca-5216-a2d1-40e7a211f0ab), object, pointer_default(unique)]
interface ICoreWebView2ExperimentalFrame10 : IUnknown {
  /// Adds an event handler for the `LaunchingExternalUriScheme` event.
  /// The event is raised when content in this `CoreWebView2Frame`, or in a
  /// descendant iframe that does not have a closer tracked
  /// `CoreWebView2Frame` ancestor, attempts to launch a URI registered
  /// with the OS as an external scheme handler.
  ///
  /// This event corresponds to `CoreWebView2.LaunchingExternalUriScheme`.
  /// For iframe-initiated launches, `CoreWebView2Frame` handlers are
  /// invoked before `CoreWebView2` handlers. If the `Handled` property on
  /// `ICoreWebView2LaunchingExternalUriSchemeEventArgs2` is set to `TRUE`
  /// within a `CoreWebView2Frame` handler, the event is not raised on
  /// `CoreWebView2`, and its handlers are not invoked.
  ///
  /// If a deferral is not taken, the external URI scheme launch is blocked
  /// until the handler returns. If a deferral is taken, the launch remains
  /// blocked until the deferral is completed.
  ///
  /// To prevent `CoreWebView2` handlers from being invoked, `Handled` must
  /// be set synchronously before taking a deferral.
  HRESULT add_LaunchingExternalUriScheme(
      [in] ICoreWebView2ExperimentalFrameLaunchingExternalUriSchemeEventHandler*
          eventHandler,
      [out] EventRegistrationToken* token);

  /// Removes an event handler previously added by
  /// `add_LaunchingExternalUriScheme`.
  HRESULT remove_LaunchingExternalUriScheme(
      [in] EventRegistrationToken token);
};

/// Receives `LaunchingExternalUriScheme` events raised on
/// `CoreWebView2Frame`.
[uuid(8d0a4bee-a888-50bc-8088-a71678fd3af3), object, pointer_default(unique)]
interface ICoreWebView2ExperimentalFrameLaunchingExternalUriSchemeEventHandler
    : IUnknown {
  /// Invoked when the corresponding event is raised.
  HRESULT Invoke(
      [in] ICoreWebView2Frame* sender,
      [in] ICoreWebView2LaunchingExternalUriSchemeEventArgs2* args);
};

/// Extends `ICoreWebView2LaunchingExternalUriSchemeEventArgs` with a
/// `Handled` property.
[uuid(126db12c-f6dc-51b7-afa4-3eecb5304b9f), object, pointer_default(unique)]
interface ICoreWebView2LaunchingExternalUriSchemeEventArgs2
    : ICoreWebView2LaunchingExternalUriSchemeEventArgs {
  /// By default, the `LaunchingExternalUriScheme` event is raised on both
  /// `CoreWebView2Frame` and `CoreWebView2`, with frame-level handlers
  /// invoked first. Set this property to `TRUE` within a
  /// `CoreWebView2Frame` handler to prevent the event from being raised on
  /// `CoreWebView2`.
  ///
  /// If a deferral is taken, this property must be set to `TRUE`
  /// synchronously before taking the deferral to prevent
  /// `CoreWebView2` handlers from being invoked.
  [propget] HRESULT Handled([out, retval] BOOL* value);

  /// Sets the `Handled` property.
  [propput] HRESULT Handled([in] BOOL value);
};

.Net/C#

namespace Microsoft.Web.WebView2.Core
{
    runtimeclass CoreWebView2LaunchingExternalUriSchemeEventArgs
    {
        // ...

        [interface_name(
            "Microsoft.Web.WebView2.Core.ICoreWebView2LaunchingExternalUriSchemeEventArgs2")]
        {
            [doc_string(
                "Set this property to TRUE to prevent the "
                "LaunchingExternalUriScheme event from being raised on "
                "CoreWebView2. By default, the event is raised on both "
                "CoreWebView2Frame and CoreWebView2, with frame-level "
                "handlers invoked first. If a deferral is taken, this "
                "property must be set to TRUE synchronously before "
                "taking the deferral.")]
            Boolean Handled { get; set; };
        }
    }

    runtimeclass CoreWebView2Frame
    {
        // ...

        [interface_name(
            "Microsoft.Web.WebView2.Core.ICoreWebView2ExperimentalFrame10")]
        {
            [doc_string(
                "The LaunchingExternalUriScheme event is raised when "
                "content in this CoreWebView2Frame, or in a descendant "
                "iframe that does not have a closer tracked "
                "CoreWebView2Frame ancestor, attempts to launch a URI "
                "registered with the OS as an external scheme handler. "
                "Frame-level handlers are invoked before CoreWebView2 "
                "handlers. Set Handled to TRUE in the frame handler to "
                "prevent CoreWebView2 handlers from being invoked.")]
            event Windows.Foundation.TypedEventHandler<
                CoreWebView2Frame,
                CoreWebView2LaunchingExternalUriSchemeEventArgs>
                LaunchingExternalUriScheme;
        }
    }
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — applied the rewrite. Adopted the structure and wording, kept the reference links and the Appendix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

API Proposal Review WebView2 API Proposal for review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants