Skip to content

Commit

Permalink
Feature: Initial support for background worker generator
Browse files Browse the repository at this point in the history
  • Loading branch information
mingyaulee committed Jul 2, 2024
1 parent dd083e6 commit c26e9eb
Show file tree
Hide file tree
Showing 54 changed files with 1,519 additions and 126 deletions.
9 changes: 8 additions & 1 deletion Blazor.BrowserExtension.sln
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{50F0A688-7
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Docs", "docs\Docs.csproj", "{8993E780-2AE5-4F7D-8B03-424254A0B821}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blazor.BrowserExtension.Build.Test", "test\Blazor.BrowserExtension.Build.Test\Blazor.BrowserExtension.Build.Test.csproj", "{86CC76BA-BA2E-42F4-82EF-485EE5F9CA2D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blazor.BrowserExtension.Build.Test", "test\Blazor.BrowserExtension.Build.Test\Blazor.BrowserExtension.Build.Test.csproj", "{86CC76BA-BA2E-42F4-82EF-485EE5F9CA2D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blazor.BrowserExtension.Analyzer", "src\Blazor.BrowserExtension.Analyzer\Blazor.BrowserExtension.Analyzer.csproj", "{50E3B676-97C3-4A7B-BC19-054F42B570A5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -82,6 +84,10 @@ Global
{86CC76BA-BA2E-42F4-82EF-485EE5F9CA2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{86CC76BA-BA2E-42F4-82EF-485EE5F9CA2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{86CC76BA-BA2E-42F4-82EF-485EE5F9CA2D}.Release|Any CPU.Build.0 = Release|Any CPU
{50E3B676-97C3-4A7B-BC19-054F42B570A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{50E3B676-97C3-4A7B-BC19-054F42B570A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{50E3B676-97C3-4A7B-BC19-054F42B570A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{50E3B676-97C3-4A7B-BC19-054F42B570A5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -96,6 +102,7 @@ Global
{E47D8D0D-3AD6-45A6-8D21-96846101EDBB} = {E8E8EF7A-5CC1-4EE3-88D3-39CB2149988E}
{8993E780-2AE5-4F7D-8B03-424254A0B821} = {50F0A688-7FC1-4075-BDDD-2AFA680FBF09}
{86CC76BA-BA2E-42F4-82EF-485EE5F9CA2D} = {6D3207F4-EC87-4CF6-A36A-F847D03F72DC}
{50E3B676-97C3-4A7B-BC19-054F42B570A5} = {E8E8EF7A-5CC1-4EE3-88D3-39CB2149988E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {500E229A-A512-45E3-B66E-46EB228929D3}
Expand Down
7 changes: 4 additions & 3 deletions docs/Pages/01_02_ManifestV3.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Firefox's implementation has slight differences with Chromium based browsers in
- Update `background`
```json
"background": {
"service_worker": "BackgroundWorker.js",
"service_worker": "content/BackgroundWorker.js",
"type": "module"
},
```
Expand All @@ -51,8 +51,9 @@ Firefox's implementation has slight differences with Chromium based browsers in
}
]
```
0. Move all the codes from `Background.razor` to `BackgroundWorker.js`
- Create a new file `BackgroundWorker.js` in `wwwroot`.
0. Move all the codes from `Background.razor` to `BackgroundWorker.cs`
- Create a new file `BackgroundWorker.cs`.
- Refer to the [Background Worker page](03_02_BackgroundWorker.md) for the sample class.
- Remove `Background.razor`
- Refer to [this guide](https://developer.chrome.com/docs/extensions/mv3/migrating_to_service_workers/) for migrating from background page to background service worker.

Expand Down
46 changes: 32 additions & 14 deletions docs/Pages/01_06_SetupExistingProject.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
0. Add `<BrowserExtensionBootstrap>true</BrowserExtensionBootstrap>` under the `<PropertyGroup>` node in your `.csproj` project file.
0. Build the project.
0. This should automatically set up the project files to be compatible for building into browser extension and the `BrowserExtensionBootstrap` property is removed from the `.csproj` project file.
0. Ideally you should be able to repeat these steps countless times. However, if it fails, you can manually set up parts of the project by referring to the steps below.

## Manual Setting Up

Expand All @@ -17,7 +18,7 @@ You can set up the project manually as well, if for some reason you encounter an
"description": "My browser extension built with Blazor WebAssembly",
"version": "0.1",
"background": {
"service_worker": "BackgroundWorker.js",
"service_worker": "content/BackgroundWorker.js",
"type": "module"
},
"content_security_policy": {
Expand All @@ -40,27 +41,44 @@ You can set up the project manually as well, if for some reason you encounter an
@page "/index.html"
@inherits IndexPage
```
0. Add a `BackgroundWorker.js` file under `wwwroot` directory, with the following content:
```js
// Import for the side effect of defining a global 'browser' variable
import * as _ from "/content/Blazor.BrowserExtension/lib/browser-polyfill.min.js";

browser.runtime.onInstalled.addListener(() => {
const indexPageUrl = browser.runtime.getURL("index.html");
browser.tabs.create({
url: indexPageUrl
});
});
0. Add a `BackgroundWorker.cs` class, with the following content.
Add `using Blazor.BrowserExtension;` statement if needed.
```csharp
public partial class BackgroundWorker : BackgroundWorkerBase
{
[BackgroundWorkerMain]
public override void Main()
{
WebExtensions.Runtime.OnInstalled.AddListener(OnInstalled);
}

async Task OnInstalled()
{
var indexPageUrl = await WebExtensions.Runtime.GetURL("index.html");
await WebExtensions.Tabs.Create(new()
{
Url = indexPageUrl
});
}
}
```
0. Add the following into the `Program.cs` file to wrap the `RootComponents` setup.
Add `using Blazor.BrowserExtension;` statement if needed.
```csharp
public static async Task Main(string[] args)
{
...
builder.UseBrowserExtension(browserExtension =>
{
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
if (browserExtension.Mode == BrowserExtensionMode.Background)
{
builder.RootComponents.AddBackgroundWorker<BackgroundWorker>();
}
else
{
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
}
});
...
}
Expand Down
57 changes: 28 additions & 29 deletions docs/Pages/03_02_BackgroundWorker.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,28 @@ Chrome calls it extension service worker in manifest V3 whereas Mozilla calls it

Background worker can be used to support the complexity in browser extensions. The pattern of content scripts, popup, or any other page communicating with the background worker to get data or instructions resembles a client-server communication.

A default background worker is created in your project.

```javascript
// Import for the side effect of defining a global 'browser' variable
import * as _ from "/content/Blazor.BrowserExtension/lib/browser-polyfill.min.js";

browser.runtime.onInstalled.addListener(() => {
const indexPageUrl = browser.runtime.getURL("index.html");
browser.tabs.create({
url: indexPageUrl
});
});
A default background worker is created in your project, if you have used the project template to create a new project, or used bootstrap for existing project.

Otherwise, you can create a new `BackgroundWorker.cs` class with the content below.

```csharp
public partial class BackgroundWorker : BackgroundWorkerBase
{
[BackgroundWorkerMain]
public override void Main()
{
WebExtensions.Runtime.OnInstalled.AddListener(OnInstalled);
}

async Task OnInstalled()
{
var indexPageUrl = await WebExtensions.Runtime.GetURL("index.html");
await WebExtensions.Tabs.Create(new()
{
Url = indexPageUrl
});
}
}
```

In the manifest, the background worker is declared in the `background` key.
Expand All @@ -26,7 +36,7 @@ In the manifest, the background worker is declared in the `background` key.
{
...
"background": {
"service_worker": "BackgroundWorker.js",
"service_worker": "content/BackgroundWorker.js",
"type": "module"
},
...
Expand All @@ -42,31 +52,20 @@ The implementation in Firefox requires a small adjustment in the `manifest.json`
{
...
"background": {
"scripts": ["BackgroundWorker.js"],
"scripts": ["content/BackgroundWorker.js"],
"type": "module"
},
...
}
```

## Why can't we use C# or other wasm in background worker?

The background worker is executed when the extension is first loaded and shut down when it is inactive, see [service worker lifecycle](https://developer.chrome.com/docs/extensions/develop/concepts/service-workers/lifecycle).
This requires the background worker to register all event listeners synchronously.

Quoted from [Chrome page](https://developer.chrome.com/docs/extensions/develop/concepts/service-workers/events)

> Event handlers in service workers need to be declared in the global scope, meaning they should be at the top level of the script and not be nested inside functions. This ensures that they are registered synchronously on initial script execution, which enables Chrome to dispatch events to the service worker as soon as it starts.

Quoted from [MDN page](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Background_scripts)
## Where does this `content/BackgroundWorker.js` come from?

> Listeners must be registered synchronously from the start of the page.
>
> Do not register listeners asynchronously, as they will not be properly triggered.
>
> Listeners must be at the top-level to activate the background script if an event is triggered. Registered listeners may need to be restructured to the synchronous pattern and moved to the top-level.
The `BackgroundWorker.js` file is generated by this package in two steps.

Therefore, we are unable to use wasm in the background worker, as it would require us to use the asynchronous `fetch` API to get the `.wasm` file stream to instantiate the dotnet runtime.
1. The `Blazor.BrowserExtension.Analyzer` package is responsible for reading the code in `BackgroundWorker.cs` and translate it into JavaScript, storing it temporarily in the file `obj/BackgroundWorkerMain.generated.js`.
0. The `Blazor.BrowserExtension` build resolves all the imported modules and generates the file `content/BackgroundWorker.js`.


# Reference
Expand Down
Loading

0 comments on commit c26e9eb

Please sign in to comment.