wpfchromiumpostmessagewebview2

window.postMessage VS window.chrome.webview.postMessage


How is window.postMessage different from window.chrome.webview.postMessage?

I'm using it in the context of a WebView2 (in a .NET/WPF application). We are loading a third-party webpage in WebView2. The webpage fires window.postMessage with certain data that we need to catch. According to the documentation, WebView2 raises WebMessageReceived event when the loaded page fires window.chrome.webview.postMessage, which is correct. On the other hand, it doesn't seem to respect window.postMessage (no WebMessageReceived is fired).

Are these two different APIs? Which one is standard (as in HTML5)? How can I catch window.postMessage event in my .NET application since I can't ask the third-party website to change their code to use window.chrome.webview.postMessage.


Solution

  • The chrome.webview.postMessage method is a WebView2 specific JavaScript function that allows web content to send messages to the host app via the CoreWebView2.WebMessageReceived event. It is patterned after the HTML standard window.postMessage DOM API but is different in a few ways beyond the big difference that chrome.webview.postMessage posts messages to the host app and window.postMessage posts messages to other windows:

    window.postMessage(message, targetOrigin, [transfer]);
    window.chrome.webview.postMessage(message);
    

    Differences

    Message parameter

    They both take a message object as their first parameter to be passed by value to the target. The message is serialized by window.postMessage using the Structured Clone Algorithm (SCA) whereas window.chrome.webview.postMessage uses JSON. The largest difference between the two is SCA can handle reference cycles in the message object, whereas JSON will throw.

    Origin parameter

    The window.postMessage method is for posting messages to other windows and there is a risk that the window may not be on the correct origin and is not allowed to receive the message data. The origin parameter allows the caller to specify the origin that is allowed to receive the data.

    In the case of WebView2 there is no such parameter as the message is being sent to the host application which is inherently trusted (its trusted since it is hosting the web content and is able to inspect the user data folder, inject script, and so on).

    Transfer parameter

    The optional transfer parameter to window.postMessage allows specific types of JavaScript objects to be moved to the target window. This includes things like message ports, and large arrays of data.

    This scenario is not currently supported by WebView2 and likewise chrome.webview.postMessage does not have this parameter.

    Forwarding to chrome.webview.postMessage

    If you want to have window.postMessage messages sent to the host app, the easiest way would be to subscribe to the window.onmessage event of the target window

    window.addEventListener("message", e => {
        if (shouldForwardToWebView2(e)) {
            window.chrome.webview.postMessage(e.data);
        }
    });
    

    If you cannot subscribe to that event you could also shim the window.postMessage method:

    const originalPostMessage = window.postMessage.bind(window);
    window.postMessage = function (message, origin, transfer) {
        if (shouldForwardToWebView2(message)) {
            window.chrome.webview.postMessage(message);
        }
        originalPostMessage(message, origin, transfer);
    };
    

    Shimming a method like this is trickier since any errors in this shim code could break anything trying to use postMessage, unlike the event handler where failures are scoped to just that event handler.

    In either case you may want to filter the messages forwarded on to WebView2 to ensure you're not sending unrelated messages from other libraries or origins you don't expect to send data to limit attack surface.

    Additionally, this will only work if the messages you want to forward to WebView2 don't depend on transfer.