Use reflex-dom and ghcjs to produce a browser extension
This library provides:
- A way to define an extension's API (
Web.Browser.Extension.Interface). The API includes both a "public" part and an "internal" part. The public part of the API is used for communication with the content script (the part of the extension that communicates directly with webpages), and the internal part is used for communications between the more secure parts of the extension (the "popup" that opens when a user clicks the extension icon, the "background script" that runs behind the scenes, and any additional popups opened by the background script). - Typeclasses that describe the capabilities of extension components, including:
Web.Browser.Extension.Storagecorresponding to the extension storage APIWeb.Browser.Extension.Messagecorresponding to the extension message-passing interface.Web.Browser.Extension.Popupcorresponding to the window creation APIWeb.Browser.Extension.Logcorresponding toconsole.log
- A way to simulate a browser extension (see
Web.Browser.Extension.Simulator) by mocking up extension-specific APIs using regular web APIs available to all webpages. This allows for rapid development of an extension in Obelisk.
This library currently does not help much with writing an extension's content script. The content script, which communicates with webpages, is not currently generated from Haskell. Instead, the repo includes a simple javascript content script that forwards messages from the webpage to the background script and forwards replies from the background script to the webpage. See assets/content.js for more information.
See MDN's Anatomy of an Extension for more on these components.
For an example browser extension that uses obelisk as a simulator and development environment, check out obelisk-browser-extension-example.
nix-shell --run ./watch.shRunning nix-build . -A extension will produce an extension bundle. Load this as an unpacked extension in chromium or firefox.
With the extension installed, you can open a browser console on any page an start sending messages to the extension.
Open an inspector for the browser extension itself by going to chrome://extensions and then clicking "Inspect views for service worker".
The bundled content script just forwards everything received by the message event listener to the extension. You'll probably see a lot of messages like [Debug] onMessage: private: parsing (a, b) failed, expected Array, but encountered String in the service worker inspector, because other javascript (e.g., js running on webpages) may be sending messages using the same message-passing API. The extension rejects any messages it cannot parse.
Open a developer console on any webpage to communicate with the browser extension. Below, we send an arbitrary message to the extension, which it rejects:
The public api of the example extension is defined in Web.Browser.Extension.Example. The message we send to the extension should be a json-encoded public api request. For example the command window.postMessage(["Pub_Test",[]]) should send a public request to the extension and cause it to open a popup.
You'll notice that the extension inspector prints [Debug] onMessage: private: Expected tag to be one of [Priv_Test] but got: Pub_Test when you send the public request. This is because both the public and private request handlers are listening to the same message event, and both try parsing incoming messages.


