Skip to content

[Problem/Bug]: [Regression] Navigation from pages rendered with custom scheme fails #5495

@ColoredCarrot

Description

@ColoredCarrot

What happened?

If you render a page from a custom scheme (like myapp://example/index.html) by intercepting WebResourceRequested, the page loads successfully. JavaScript-powered interactivity also works.
However, it's impossible to click on any links, or to programmatically (from C++) navigate to a different page.

The attached example application works in 143.0.3650.139, but fails with the latest version.

The following error is printed in the console when trying to navigate:

[0121/093233.305:ERROR:mojo\public\cpp\bindings\lib\validation_errors.cc:112] Invalid message: VALIDATION_ERROR_DESERIALIZATION_FAILED
[0121/093233.305:ERROR:mojo\public\cpp\bindings\lib\interface_endpoint_client.cc:732] Message 1587913693 rejected by interface embedded_browser.mojom.EmbeddedBrowserClient

Importance

Blocking. My app's basic functions are not working due to this issue.

Runtime Channel

Stable release (WebView2 Runtime)

Runtime Version

144.0.3719.82

SDK Version

1.0.3240.44

Framework

Win32

Operating System

Windows 11

OS Version

26200.7623

Repro steps

Below is a minimal example CMake project that reproduces the issue. With an older WebView2 Runtime, when you click on "Go to another page", you are successfully navigated to "/asd.html". With the latest version, you stay on "/index.html".

CMakeLists.txt

cmake_minimum_required(VERSION 3.31)
project(webview2_test)

set(CMAKE_CXX_STANDARD 23)

add_executable(webview2_test WIN32 main.cpp)

find_package(wil CONFIG REQUIRED)
target_link_libraries(webview2_test PRIVATE WIL::WIL)

find_package(unofficial-webview2 CONFIG REQUIRED)
target_link_libraries(webview2_test PRIVATE unofficial::webview2::webview2)

target_link_libraries(webview2_test PRIVATE shlwapi)

vcpkg.json

{
  "name": "webview2-test",
  "version-string": "1.0.0",
  "builtin-baseline": "f9b54c1c539dda8d61c3001bb30eb9f0c5032086",
  "dependencies": [
    "webview2",
    "wil"
  ]
}

main.cpp

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <shlwapi.h>
#include <wrl.h>
#include <wil/com.h>
#include <wil/result.h>
#include <WebView2.h>
#include <WebView2EnvironmentOptions.h>

#include <format>
#include <string>
#include <string_view>

#pragma comment(lib, "shlwapi.lib")

using Microsoft::WRL::Callback;
using Microsoft::WRL::ComPtr;

std::string to_utf8( std::wstring_view wstr )
{
    if (wstr.empty()) return {};

    int size_needed = WideCharToMultiByte( CP_UTF8, 0, wstr.data(), static_cast<int>(wstr.size()), nullptr, 0, nullptr,
                                           nullptr );
    FAIL_FAST_IF( size_needed <= 0 );

    std::string result( size_needed, 0 );
    WideCharToMultiByte( CP_UTF8, 0, wstr.data(), static_cast<int>(wstr.size()), result.data(), size_needed, nullptr,
                         nullptr );
    return result;
}


wil::com_ptr<ICoreWebView2Controller> webviewController;
wil::com_ptr<ICoreWebView2>           webviewWindow;

LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
    switch (message)
    {
    case WM_SIZE:
        if (webviewController)
        {
            RECT bounds;
            GetClientRect( hWnd, &bounds );
            webviewController->put_Bounds( bounds );
        }
        break;
    case WM_DESTROY:
        PostQuitMessage( 0 );
        break;
    default:
        return DefWindowProc( hWnd, message, wParam, lParam );
    }
    return 0;
}

void InitializeWebView( HWND hWnd )
{
    auto options = Microsoft::WRL::Make<CoreWebView2EnvironmentOptions>();

    // Register a custom scheme
    auto myScheme = Microsoft::WRL::Make<CoreWebView2CustomSchemeRegistration>( L"myapp" );
    myScheme->put_HasAuthorityComponent( TRUE );

    ICoreWebView2CustomSchemeRegistration* registrations[] = { myScheme.Get() };
    FAIL_FAST_IF_FAILED( options->SetCustomSchemeRegistrations(1, registrations) );

    FAIL_FAST_IF_FAILED( CreateCoreWebView2EnvironmentWithOptions(
        nullptr, nullptr, options.Get(),
        Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
            [hWnd](HRESULT result, ICoreWebView2Environment* env) -> HRESULT {
            FAIL_FAST_IF_FAILED(result);

            env->CreateCoreWebView2Controller(hWnd,
                Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
                    [hWnd, env = wil::com_ptr<ICoreWebView2Environment>(env)](HRESULT result, ICoreWebView2Controller*
                        controller) -> HRESULT {
                    FAIL_FAST_IF_FAILED(result);

                    webviewController = controller;
                    FAIL_FAST_IF_FAILED(webviewController->get_CoreWebView2(&webviewWindow));

                    RECT bounds;
                    GetClientRect(hWnd, &bounds);
                    webviewController->put_Bounds(bounds);

                    /////////////////////////////////////////////////////////////////////////
                    ///  The important part: Register a WebResourceRequested filter
                    FAIL_FAST_IF_FAILED(webviewWindow->AddWebResourceRequestedFilter(
                        L"myapp://*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL));

                    FAIL_FAST_IF_FAILED(webviewWindow->add_WebResourceRequested(
                        Callback<ICoreWebView2WebResourceRequestedEventHandler>(
                            [env](ICoreWebView2* sender, ICoreWebView2WebResourceRequestedEventArgs* args) -> HRESULT {
                            wil::com_ptr<ICoreWebView2WebResourceRequest> request;
                            FAIL_FAST_IF_FAILED(args->get_Request(&request));

                            wil::unique_cotaskmem_string uri;
                            FAIL_FAST_IF_FAILED(request->get_Uri(&uri));

                            // Construct a simple response that contains the current URL
                            // as well as a link to a different page
                            std::string content = std::format(
                                R"(<html><body><p>You are on {}</p><p><a href="/asd.html">Go to another page</a></p></body></html>)"
                                ,
                                to_utf8(std::wstring_view(uri.get())));

                            wil::com_ptr<IStream> stream;
                            stream.attach(SHCreateMemStream(
                                reinterpret_cast<const BYTE*>(content.data()),
                                static_cast<UINT>(content.size())));

                            wil::com_ptr<ICoreWebView2WebResourceResponse> response;
                            FAIL_FAST_IF_FAILED(env->CreateWebResourceResponse(
                                stream.get(), 200, L"OK", L"Content-Type: text/html", &response));

                            FAIL_FAST_IF_FAILED(args->put_Response(response.get()));
                            return S_OK;
                            }).Get(), nullptr));

                    FAIL_FAST_IF_FAILED(webviewWindow->Navigate(L"myapp://example/index.html"));

                    return S_OK;
                    }).Get());
            return S_OK;
            }).Get()) );
}

int APIENTRY WinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine,
                      _In_ int       nCmdShow )
{
    FAIL_FAST_IF_FAILED( CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED) );

    WNDCLASSEXW wcex = {};
    wcex.cbSize = sizeof( WNDCLASSEX );
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.hInstance = hInstance;
    wcex.hCursor = LoadCursor( nullptr, IDC_ARROW );
    wcex.lpszClassName = L"WebView2WindowClass";
    RegisterClassExW( &wcex );

    HWND hWnd = CreateWindowW( L"WebView2WindowClass", L"WebView2 Regression Repro", WS_OVERLAPPEDWINDOW,
                               CW_USEDEFAULT, CW_USEDEFAULT, 1024, 768, nullptr, nullptr, hInstance, nullptr );

    FAIL_FAST_IF_NULL( hWnd );

    ShowWindow( hWnd, nCmdShow );
    UpdateWindow( hWnd );

    InitializeWebView( hWnd );

    MSG msg;
    while (GetMessage( &msg, nullptr, 0, 0 ))
    {
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }

    return (int) msg.wParam;
}

Repros in Edge Browser

No, issue does not reproduce in the corresponding Edge version

Regression

Regression in newer Runtime

Last working version (if regression)

Runtime 143.0.3650.139

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingregressionSomething used to work but doesn't anymore

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions