Debugging Javascript code for WebView

I’m dealing with the WebViewPluginDemo_Standalone in examples/Plugins/WebViewPluginDemo. What I want to do is debugging a Javascript code with native function enabled, but when running a browser with npm, the UI loads fine but the Javascript can’t find the native functions(ofcourse).

Is there any way to debug a WebView Javascript in JUCE 8?

In a Debug build of the JUCE project, you should be able to right-click in the webview and “inspect” to open the debug view.

1 Like

Wow, didn’t know that. Thanks!

It is useful, but we have some some complex JS logic(E.g., asynchronous communication) that is kinda hard to debug using the inspector.
Is it possible to attach a debugger, setting a breakpoint and run code line by line like the way frontend developers do?

WebView implementations, and the underlying browser engine, will generally offer remote debugging capabilities. This means that your development environment can connect to them over a network address and port, so you can place breakpoints in the IDE and inspect the stack when they are triggered.

This is relatively easy to set up on Windows with Visual Studio Code + Edge or Chromium. This page describes how this can be configured Debug WebView2 apps with Visual Studio Code - Microsoft Edge Developer documentation | Microsoft Learn

It should be possible to set this up on MacOS as well, but you’ll have to search for OS / Webkit specific instructions.

I’ve used this with the Android emulator too, I think the Android docs cover how to do it. You may have to change the remote debugging port in the Chrome developer settings, but usually this will work out of the box.

Also, the Windows + VS Code approach integrates nicely with npm. You can run npm from inside the VS Code terminal. You can uncomment line 604 in the WebViewPluginDemo so that it connects to the local npm dev server. Then you configure VS Code with launch.json and specify the WebViewPluginDemo standalone executable as the launch target.

Then you can modify App.js, hit F5 and it will launch or relaunch the WebViewPluginDemo and immediately stop at any breakpoints that you have in VS Code.

2 Likes

I spent a couple of hours setting up WebView2 debugging with VS Code on Windows, and I want to share my experience with you.

Documentation

The WebView2 VS Code debugging page have some outdated and missing information. I corrected it and sent a Pull Request to them. See the description of the PR about the changes.

WebView2 and source maps

WebView2 does not load separate JS, CSS or other source maps which are referenced by content files served through the WebResourceRequested API. This WebResourceRequested API is used by JUCE’s ResourceProvider too. This is a known limitation,
which WebView2 maintainers seems to be not willing to fix. Edge (Chromium) uses a different internal mechanism to download source maps than to other resources. That is why, the ResourceProvider is not called to serve any source map files. Instead, you can see the following message in the Debug Console:

Could not read source map for https://juce.backend/main.js: Unexpected 503 response from https://juce.backend/main.js.map: getaddrinfo ENOTFOUND juce.backend

VS Code provides several properties in launch.json to configure locations of source maps (resolveSourceMapLocations, sourceMapPathOverrides, outFiles), but in practice none of them worked for me. (any idea anyone???)

I created a PR to WebView2 documentation about this issue too.

Workarounds

  • Use external web server: it is rather easy. Native integration (functions and events between JS and C++) works without ResourceProvider. So in debug mode, omit the WebBrowserComponent::Options::withResourceProvider(…) call and make the browser component load the URL of your local web server. This results in a very convenient development / debugging experience (e.g. hot-reload), but you will not debug the “real thing” served by your ResourceProvider.
  • Inline source maps: source maps can be embedded to JS, CSS etc. files. TypeScript offers the ”inlineSourceMap”: true property in tsconfig.json. Many build/packaging systems and web frameworks offer that functionility, but not all. Refer to the documentation of the packaging system or framework of your choice!
  • Merge source maps on the fly: as a last resort, you can merge your separate source maps to the JS files in the ResourceProvider. Here you can find how to do that.
    This may be necessary, if you use a web framework like Angular which does not support inline source map generation.

One-click C++ and JS debug

If you - like me - want to automate as many things as possible in your developer workflow, you might like this :slight_smile:

I put together a compound configuration in launch.json, which starts the development web server, builds and launches the JUCE app, and then automatically attaches the msedge debugger to the app process. This way both C++ and JS can be debugged with a single click.

The only trick here, is that the msedge attach config shall wait until the app is built and started. That can be solved with a preLaunchTask which runs a Powershell script. The script periodically checks if your app process started and exists once it find it.

launch.json:

{
    "version": "0.2.0",
    "configurations": [    
        {
            "name": "[Windows] Web Attach after Standalone startup",
            "type": "msedge",
            "port": 9222,
            "request": "attach",
            "webRoot": "${workspaceFolder}/your/web/folder",
            "preLaunchTask": "[Windows] Wait for Standalone "
        },
        {
            "name": "[Windows] Standalone with Web Serve",
            "type": "cppvsdbg",
            "request": "launch",
            "program": "${workspaceRoot}/path/to/your/built/standalone_executable.exe",
            "preLaunchTask": "[Windows] CMake Build Debug + NPM Serve",
            "stopAtEntry": false,
            "cwd": "${workspaceRoot}",
            "console": "integratedTerminal",
            "environment": [
                {
                    // Check this env var in your C++ code and use this URL instead of ResourceProvider
                    "name": "USE_WEBSERVER",
                    "value": "http://localhost:4200"
                },
                {
                    "name": "WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS",
                    "value": "--remote-debugging-port=9222"
                }
            ]
        }
    ],
    "compounds": [
        {
            "name": "[Windows] Standalone with Web Serve + Web Attach",
            "stopAll": true,
            "configurations": [
                "[Windows] Standalone with Web Serve",                
                "[Windows] Web Attach after Standalone startup"
            ]
        }
    ]
} 

tasks.json:

{
    "version": "2.0.0",
    "tasks": [     
         {
            "label": "[Windows] Wait for Standalone ",
            "type": "shell",
            "command": "${workspaceFolder}/scripts/ahp-wait-process.ps1",
            "args": [
                "standalone_executable*"
            ],
            "group": "none",
        },
        {
            "label": "[Windows] CMake Build Debug + NPM Serve",
            "dependsOrder": "sequence",
            "dependsOn": [
                "[Windows] CMake Build Debug",
                "NPM Serve",
            ]
        },
        {
            "label": "NPM Serve",
            // Start here your development web server
            ...
        },
        {
            "label": "[Windows] CMake Build Debug",
            // Build your JUCE app here
            ...
        },

ahp-wait-process.ps1:

param
(
    [String]
    [Parameter(Mandatory = $true)]
    $Name
)

$existingCount = (Get-Process -Name $Name -ErrorAction SilentlyContinue).Count

Write-Host "Waiting for $Name" -NoNewline
while ($true)
{
    $count = (Get-Process -Name $Name -ErrorAction SilentlyContinue).Count
    if($count -gt $existingCount)
    {
        break
    }
    Write-Host '.' -NoNewline
    Start-Sleep -Milliseconds 400
}

Write-Host "`n$Name" is running
4 Likes

Has anyone gotten this working on Mac?
Edit: Figured out what was going wrong. I was using CMake with Ninja, I had to use Xcode (still CMake; cmake -Bbuild -G Xcode).

Any advice / best practices for debugging on macOS inside an IDE (ideally VSCode)?

Is it possible to do end-to-end step debugging? E.g. set a breakpoint in C++ code, another in JS and see each one fire sequentially?