A package for consuming WebExtensions API in a browser extension.
These API classes are generated based on the Mozilla documentation for WebExtensions API.
This package can be consumed in two methods.
Create a browser extension using Blazor. Refer to the package Blazor.BrowserExtension to get started.
Create a standard browser extension using JavaScript and load the WebAssembly manually. The .Net source code can be compiled into wasm using Mono.
- Install
WebExtensions.Net(if you intend to use the API without dependency injection) orWebExtensions.Net.Extensions.DependencyInjectionfrom Nuget. - Import the
JsBind.Netscripts with<script src="_content/JsBind.Net/JsBindNet.js"></script>. If your project does not support Razor Class Library contents, add the property<LinkJsBindAssets>true</LinkJsBindAssets>to your project. - Import the WebExtensions polyfill by Mozilla for cross browser compatibility. This polyfill helps to convert the callback based Chrome extensions API to a Promise based API for asynchronous functions.
- Consume the WebExtensions API by creating an instance of
WebExtensionsApias shown below.
usingJsBind.Net;usingJsBind.Net.Configurations;usingWebExtensions.Net;
...var options =newJsBindOptionsConfigurator().UseInProcessJsRuntime().Options;// iJsRuntime is an instance of MonoWebAssemblyJSRuntimevarjsRuntimeAdapter=newJsRuntimeAdapter(iJsRuntime,options);varwebExtensionsApi=newWebExtensionsApi(jsRuntimeAdapter);// Use the WebExtensions APIvarmanifest=awaitwebExtensionsApi.Runtime.GetManifest();For the purpose of debugging and testing outside of the browser extension environment, there is a MockJsRuntimeAdapter class under the WebExtensions.Net.Mock namespace.
Initialize an instance of the mock API with:
usingWebExtensions.Net;usingWebExtensions.Net.Mock;
...var jsRuntimeAdapter =newMockJsRuntimeAdapter();varwebExtensionsApi=newWebExtensionsApi(jsRuntimeAdapter);To configure the behaviour of the mock API, you may use any combination of the following:
MockResolvers.Configure(configure =>{// configure a method without any argumentconfigure.Api.Property(api =>api.Runtime.Id).Returns(()=>"MyExtensionId");// orconfigure.Api.Property(api =>api.Runtime.Id).ReturnsForAnyArgs("MyExtensionId");// configure a method with one argumentconfigure.Api.Method<string,string>(api =>api.Runtime.GetURL).Returns(path =>builder.HostEnvironment.BaseAddress+path);// configure a method that returns the same object regardless of the argumentsconfigure.Api.Method<string,NotificationOptions,string>(api =>api.Notifications.Create).ReturnsForAnyArgs("NotificationId");// configure an action to be invoked when an API is calledconfigure.Api.Method<int?>(api =>api.Tabs.GoForward).Invokes(tabId =>{/* Do something with tabId */});// configure a method on an object referenceusingvaremptyJson=JsonDocument.Parse("{}");configure.ObjectReference(DefaultMockObjects.LocalStorage).Method<StorageAreaGetKeys,JsonElement>(storage =>storage.Get).ReturnsForAnyArgs(emptyJson.RootElement.Clone());// configure an action to be invoked when a method on an object reference is calledconfigure.ObjectReference(DefaultMockObjects.LocalStorage).Method(storage =>storage.Clear).Invokes(()=>{/* Do something */});// configure a generic delegate to handle all the API callsboolapiHandler(stringtargetPath,object[]arguments,outobjectresult){if(targetPath=="runtime.id"){result="MyExtensionId";returntrue;}result=null;returnfalse;}configure.ApiHandler(apiHandler);// configure a generic delegate to handle all the invocations to object referencesboolobjectReferenceHandler(objectobjectReference,stringtargetPath,object[]arguments,outobjectresult){if(objectReference==DefaultMockObjects.LocalStorage&&targetPath=="get"){usingvaremptyJson=JsonDocument.Parse("{}");result=emptyJson.RootElement.Clone();returntrue;}result=null;returnfalse;}configure.ObjectReferenceHandler(objectReferenceHandler);});Note: The sequence of mock registration matters.
Overall the more specific method Returns and ReturnsForAnyArgs is prioritized over the generic delegate ApiHandler and ObjectReferenceHandler.
If there exists registration that handles the same API or object reference call, the last registered handler will be used.
For example:
MockResolvers.Configure(configure =>{// when api.Runtime.GetId is called it will return "MyExtensionId2"configure.Api.Property(api =>api.Runtime.Id).Returns(()=>"MyExtensionId1");configure.Api.Property(api =>api.Runtime.Id).Returns(()=>"MyExtensionId2");});MockResolvers.Configure(configure =>{// when api.Runtime.GetId is called it will return "MyExtensionId1", even though the generic API handler is registered last, the more specific method registration is prioritized.configure.Api.Property(api =>api.Runtime.Id).Returns(()=>"MyExtensionId1");boolapiHandler(stringtargetPath,object[]arguments,outobjectresult){if(targetPath=="runtime.id"){result="MyExtensionId2";returntrue;}result=null;returnfalse;}configure.ApiHandler(apiHandler);});