Skip to content

Extension Authoring

WebToApp stays extensible after an app ships. You can add three kinds of extensions, all managed by a single ExtensionManager and injected by the WebView at page-lifecycle hooks:

TypeWhat it isGood for
JS ModuleA module.json manifest + main.js (+ optional CSS)Custom features with a config UI and floating panel
CSS ModuleA pure style override (still needs a main.js stub)Theming, restyling, dark mode
UserscriptTampermonkey/Greasemonkey-style .user.jsPorting existing userscripts; GM_* APIs
Chrome MV3A Manifest V3 Chrome extensionPorting browser extensions; chrome.* APIs

How injection works

All four types are normalized into one internal ExtensionModule model. At runtime, WebViewManager injects each module at its configured run time:

Run timeFires at
DOCUMENT_STARTonPageStarted
DOCUMENT_ENDonPageFinished (DOMContentLoaded)
DOCUMENT_IDLEafter load (default)
CONTEXT_MENUon context menu
BEFORE_UNLOADbefore unload

Each module also carries URL match rules (Chrome-style globs or regex) that decide which pages it runs on.

Important accuracy notes

A few behaviors differ from what other extension platforms imply. These are documented on the relevant pages, but worth knowing up front:

  • Userscript GM_* functions are not gated by @grant — all are exposed unconditionally.
  • Chrome ISOLATED vs MAIN world is not truly isolated — Android WebView has a single JS context; isolation is simulated.
  • GM_notification only logs; the MV3 "background service worker" is a hidden WebView, not a real service worker.

Where to go next

Released under the Unlicense.