packages/web/src/routes/docs/contributors/chrome-extension/+page.md
Harper's Chrome extension is still in its infancy. At a high level, there are just three components: the content script, the options page and the popup "page".
At the moment, this document is also in its infancy. It is incomplete, and we would really appreciate contributions to make it better.
The content script has three responsibilities:
All three of these responsibilities are handled by the lint-framework package.
Notably, it does not do any linting itself. Instead, it submits requests to the background worker to do so, since instantiating a WebAssembly module on every page load is expensive.
At the moment, the popup page has just one functional button that toggles Harper on the current domain. Again, it doesn't interact with local storage itself to do this. Rather, it initiated requests to the background worker, which then interfaces with local storage.
Similar to the popup page, the options page initiates requests to the background worker to change the extensions configuration. It has settings for:
It will eventually allow users to clear ignored suggestions and configure their dictionary.
This is the location of a lot of centralized "business" logic. It:
harper.js and performs lintingDespite the name of the package, the chrome-plugin also supports Firefox.
To build for Firefox, just use pnpm zip-for-firefox or otherwise compile with the environment variable TARGET_BROWSER=firefox.
Google Docs is a complex beast. Used by billions around the world, it's honestly an engineering marvel. Naturally, we want to allow Harper users to use that marvel if they so wish. That posed a complex problem for us, which is why many of our early users may remember a (relatively) long era where we simply didn't support Google Docs.
Google Docs was difficult to support for two simple reasons:
Our saving grace was the open-sourcing of Witty Works, an inclusive language checker. They figured out a way to access the internal document state, which we took inspiration from. Here's how it works.
At a high level, Harper uses three pieces:
That's the purpose of the googleDocsBootstrap.js content script.
It's short, sweet, and to the point:
@code(../../../../../../chrome-plugin/src/contentScript/googleDocsBootstrap.js)
After that, Harper injects google-docs-bridge.js into the page's main world.
We need that because the normal extension content script runs in an isolated world and cannot directly use the Google Docs APIs we rely on.
The bridge reads the document's logical text and selection state, watches for layout changes, and exposes enough information for the rest of Harper to work. Harper then mirrors that state into a hidden target element inside the editor. That hidden target gives the lint framework something stable to lint, while the visible Google Docs layout is still used to place highlights in the right location.
Suggestion application also goes back through the bridge. In other words, the bridge is the adapter between Google Docs' internal editor model and Harper's normal browser linting flow.