In my previous post, I've walked through how to migrate a JavaScript-based Chrome extension to Blazor WASM with minimal code changes. Although it's successfully migrated to Blazor WASM, it doesn't fully use the JavaScript Interoperability (JS interop) feature, which is the powerful feature of Blazor WASM. I'm going to take this feature throughout this post.
You can download the sample Chrome extension from this GitHub repository:
This provides sample codes for a Chrome Extension app built on Blazor WASM
Blazor WASM Browser Extension Sample
This provides sample codes for a cross-browser extension app built on Blazor WASM. This sample app originally started for building a Chrome extension with Blazor WASM, but it now does the cross-browser support including Chromium-based browsers and Mozilla FireFox.
The index.html file written in the previous post looks like the following. It loads blazor.webassembly.js first with the autostart="false" option, followed by loading js/main.js through the function call. The js/main.js reference is replaced with js/options.js or js/popup.js during the artifact generation process.
<!DOCTYPE html><htmllang="en">
...
<body><divid="app">Loading...</div>
...
<!-- Add the 'autostart' attribute and set its value to 'false' --><script src="_framework/blazor.webassembly.js"autostart="false"></script><!-- ⬇️⬇️⬇️ Add these lines ⬇️⬇️⬇️ --><script>Blazor.start().then(function (){varcustomScript=document.createElement('script');customScript.setAttribute('src','js/main.js');document.head.appendChild(customScript);});</script><!-- ⬆️⬆️⬆️ Add these lines ⬆️⬆️⬆️ --></body></html>
I'm not happy with this JS loading due to the two reasons below:
I have to explicitly give the option of autostart="false" while loading the blazor.webassembly.js file, which is extra to the bootstrapper.
I have to append js/main.js through the Promise pattern after Blazor.start(), which is another extra point to the bootstrapper.
Can we minimise this modification from the original index.html file and use more JS Interop capabilities here so that it can be more Blazor-ish?
Chrome Extension – JS Interop Step #1
Let's update the index.html file. Unlike the previous update, remove the JS part calling the Blazor.start() function. Load js/main.js before loading blazor.webassembly.js. Remove the autostart="false" attribute as well.
<!DOCTYPE html><htmllang="en">
...
<body><divid="app">Loading...</div>
...
<!-- ⬇️⬇️⬇️ Add this line ⬇️⬇️⬇️ --><script src="js/main.js"></script><!-- ⬆️⬆️⬆️ Add this line ⬆️⬆️⬆️ --><script src="_framework/blazor.webassembly.js"></script></body></html>
Originally the js/main.js file was blank, but this time let's add the following JS function that appends another script tag to load the given JS file reference.
Build and publish the Blazor WASM app, then run the PowerShell script to get ready for the extension loading. Once reload the extension, it works with no issue. The loadJs function is the key that takes advantage of the JS Interop feature.
However, I'm still not happy with adding js/main.js to index.html. Can we also remove this part from the file and use the JS Interop feature instead?
Chrome Extension – JS Interop Step #2
Let's get index.html back to the original state when a bootstrapper creates the file. Then, all we can see in index.html is the blazor.webassembly.js file reference.
<!DOCTYPE html><htmllang="en"><head><metacharset="utf-8"/><metaname="viewport"content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/><title>ChromeExtensionV2</title><basehref="/"/><linkhref="css/bootstrap/bootstrap.min.css"rel="stylesheet"/><linkhref="css/app.css"rel="stylesheet"/><linkhref="ChromeExtensionV2.styles.css"rel="stylesheet"/></head><body><divid="app">Loading...</div><divid="blazor-error-ui">
An unhandled error has occurred.
<ahref=""class="reload">Reload</a><aclass="dismiss">🗙</a></div><script src="_framework/blazor.webassembly.js"></script></body></html>
Then, add the export declaration in front of the loadJs function in the js/main.js file.
exportfunctionloadJs(sourceUrl){...}
Update Popup.razor like below.
...varsrc="js/popup.js";// Import the `js/main.js` filevarmodule=awaitJS.InvokeAsync<IJSObjectReference>("import","./js/main.js").ConfigureAwait(false);// Invoke the `loadJs` functionawaitmodule.InvokeVoidAsync("loadJs",src).ConfigureAwait(false);
The same change should also be applicable to Options.razor. And finally, update the manifest.json below because we no longer need the hash key for popup.js and options.js.
{"manifest_version":2,"version":"1.0","name":"Getting Started Example (Blazor WASM)","description":"Build an Extension!",..."content_security_policy":"script-src 'self' 'unsafe-eval' 'wasm-unsafe-eval' 'sha256-v8v3RKRPmN4odZ1CWM5gw80QKPCCWMcpNeOmimNL2AA='; object-src 'self'",...}
Build and publish the app, and run the PowerShell script against the artifact. Then, reload the extension, and you will see the same result.
So far, we've walked through how to take more advantage of the JS Interop feature that Blazor WASM offers to migrate the existing Chrome extension to Blazor WASM. What could be the potential merits of this exercise?
We never touch any bootstrapper codes that Blazor WASM generate for us.
If necessary, we load JavaScript for each page using the JS Interop feature. During this practice, C# handles all the JS codes.
Then, does this exercise only brings you benefits? Here are a couple of considerations:
The code gets overly complex. If we simply import the JavaScript files through the index.html/popup.html/options.html, we don't need to do this exercise.
Not everytime the dynamic JS loading is useful. It has trade-offs. If you don't want to touch the bootstrapper files, then try this approach discussed in this post. But if you do touch the bootstrapper files, then this dynamic JS loading approach may be unsuitable.
Overall, if we use more JS Interop features appropriately, we can build the Blazor WASM app more effectively, which will be another option for building Chrome extensions. In the next post, I'm going to discuss cross-browser compatibility for this Blazor WASM-based browser extension.