# ChromeXt: UserScripts and DevTools for Android browsers, via Xposed

> ChromeXt is an Xposed module that adds Tampermonkey style UserScripts and an Eruda based DevTools console to Chromium and WebView browsers on Android. It works by hooking a single URL update function and evaluating the script through the javascript: scheme or the DevTools protocol, and it installs either through LSPosed on a rooted device or by patching the browser APK.

**JingMatrix/ChromeXt** — UserScript and DevTools supports for Chromium based and WebView based browsers

- Repository: https://github.com/JingMatrix/ChromeXt
- Website: https://jingmatrix.github.io/ChromeXt/
- Stars: 1,162 · Forks: 100
- Language: Kotlin
- License: GPL-3.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/jingmatrix-chromext

## A userscript manager for browsers that do not ship one

ChromeXt adds UserScript and DevTools supports to Chromium based and WebView based browsers using the Xposed framework. The project is written in Kotlin, the licence is GPL-3.0, and the homepage is a documentation site at jingmatrix.github.io/ChromeXt.

The problem it solves is that most Android browsers do not have a userscript engine. On the desktop, Tampermonkey and its relatives are extensions; on Android, Chromium-based browsers have no extension support, so a script that patches a page on a laptop has no equivalent on a phone or tablet. ChromeXt provides one, along with a developer console for the pages that do not have one.

The supported browser list is specific and has a history in it. Bromite, Samsung Internet and Brave are fully supported. Edge is struck through in the list, so it appears to have been supported and then dropped. Most WebView based browsers are supported as well, and the project asks you to report the ones that are not.

The WebView instruction is the one people get wrong. You only need to enable the module for the browser application you want to use, not for any possible WebView application, and not for the Android system. Enabling it system wide is the route to a device that behaves strangely, and the README is unambiguous about it.

The project also publishes a topic list that names the browsers it is used with, including Bromite, Brave, Vivaldi, Thorium, Microsoft Edge and Samsung Internet, which is a good indication of where the effort has gone.

## The whole mechanism is one hooked function

The how it works section is short, and it is worth reading carefully because it explains both the power and the limits of the approach.

The project hooks the onUpdateUrl function in the UserScript.kt file in the app source, adds a URL comparison there, and evaluates JavaScript using the javascript: scheme, or the DevTools Protocol when possible.

So the flow is: a URL is about to change, the hook sees it, the hook compares the URL against the match patterns the installed scripts have declared, and if one matches, the script is evaluated. The javascript: scheme is the fallback, and the DevTools protocol is used where the browser exposes it, which is the better path because it is the mechanism a developer console would use.

That single hook is why the module is small and why it is constrained. It works at navigation time, so a script that wants to run before the page exists has to be run-at document-start, and it works by URL, so the matching rules of the script decide everything. A script that never matches a URL simply never runs.

The source layout supports the story. The app/ directory holds the Android module, with the hook in app/src/main/java/org/matrix/chromext/hook/UserScript.kt, the script runtime in app/src/main/java/org/matrix/chromext/script/Local.kt, the JavaScript side of the API in app/src/main/assets/GM.js, and the dispatch handler in app/src/main/java/org/matrix/chromext/Listener.kt. The manifest decides which browsers are opened and in what order.

## Two install routes, and only one of them is simple

ChromeXt requires the Xposed framework, and how you get there depends on whether your device is rooted.

For root users, the instruction is to install LSPosed first, then pick up the latest built APK from the repository's GitHub Actions and install it.

For non-root users, the author maintains a modified LSPatch that supports ChromeXt, and the steps are spelled out. You download the latest lspatch-release from the LSPatch GitHub Actions, and the latest ChromeXt.apk from the ChromeXt GitHub Actions, extract them to get lspatch.jar and ChromeXt-signed.apk, patch the browser APK with the command below, and then install the patched APK, which might require uninstalling the original first.

```bash
java -jar lspatch.jar arm64_ChromePublic.apk -d -v -m ChromeXt-signed.apk --force --injectdex
```

If you do not have a Java environment, the project points at a provided manager APK and the Inject loader dex option instead.

Two practical notes. Downloading from GitHub Actions requires being logged in to GitHub, so a first-time user without an account has a problem before they even reach the patch step. And the patched APK has to be signed differently, which is why the original may have to be uninstalled first and why the patched app is a separate copy of your browser rather than an update to it.

There is also a third path. The author uploads releases to the Xposed-Modules-Repo under the org.matrix.chromext name when needed, though the README notes that is not that frequent, so the module repository is not necessarily current.

## Most of the Tampermonkey API, with deliberate differences

ChromeXt supports almost all Tampermonkey APIs, and the README walks through the groups, including the places where the implementation differs from the original.

Metadata blocks such as @name, @namespace and @description are supported, with the note that colons are not allowed in the name. @match follows the Chrome standard match patterns and additionally supports regular expressions, and @include and @exclude behave the same as @match. @run-at supports document-start, document-end and document-idle, with document-idle as the default and the fallback value.

The grants are grouped. The first group covers GM_addStyle, GM_addElement, GM_xmlhttpRequest, GM_openInTab, GM_registerMenuCommand, which is shown in the Resources panel of eruda, GM_unregisterMenuCommand, GM_download, and unsafeWindow, which is window. The second covers the value and clipboard APIs, GM_setValue, GM_getValue, which the README notes is less powerful than GM.getValue, GM_listValues, the value change listeners, GM_setClipboard, GM_cookie, GM_notification and window.close.

Three differences are called out. @require and @resource work, but without Subresource Integrity. @inject-into and @sandbox are supported, and by default imported scripts using @require can define or overwrite global JavaScript objects, which @inject-into content or @sandbox DOM disables. And @noframes has a condition, since loading UserScripts for frames requires a script with @grant frames on the main page.

The project points you at Local.kt and GM.js for the details, and says plainly that these APIs are implemented differently from the official ones.

## GM.ChromeXt is powerful, and locked until you ask for it

One API is treated differently from the rest, and the reasoning is the best argument in the README.

There is the GM.ChromeXt API, described as powerful and also dangerous, which must be declared by @grant GM.ChromeXt to unlock its usage. It is locked by default so that users are protected from malicious UserScripts exploiting ChromeXt.

The API allows scripts to use the JavaScript method ChromeXt.dispatch(action, payload), which the README says is fundamental to implement other APIs, which is why usage examples can be found in GM.js. The dispatched action and payload are handled by Listener.kt.

So the design is a two tier trust model. The ordinary Tampermonkey-compatible surface is available to any script, and the surface that can reach into the browser is opt-in per script. A user who installs a script from an untrusted source has not, by default, handed it the powerful part.

That is a sensible security model for a project whose whole point is running third party code inside a browser, and it is worth noting that the same reasoning shapes the rest of the project, from the locked API to the instruction to enable the module only for the browser you actually use.

## The manager front end, the mirrors and the developer console

Managing the installed scripts is done through a front end hosted on github.io, and the README lists two mirrors in case of connection issues, one on onrender.com and one on netlify.app. The mirrors matter for users in regions where GitHub Pages is slow, and they are also a supply chain consideration, since a mirror is a third party serving the interface that manages the scripts you install.

There is a nice touch in the install flow. If you cancel the prompt to install a new UserScript, you can then edit the script directly in Chrome, and use the Install UserScript page menu to install your modified version. So a script that needs a change is a two step process rather than a matter of finding the file and editing it in a text editor.

The DevTools side comes from the three dots page menu, with two entries. The Developer tools menu opens the manager front end, and the Eruda console menu gives you a console for other pages. The Edge variant of that menu placement is struck through in the README, which suggests Edge no longer has the custom menu, and for WebView based browsers and Samsung Internet the menu items are in the context menu instead.

The bonus features are Android specific. ChromeXt can receive shared texts and search them using Google, and it can open JavaScript files to install them as UserScripts, and the reversed priority order for opening Chromium browsers is set in AndroidManifest.xml. There is also work on the system gesture conflict, since the history forward gesture of Chrome sits near the vertical edge. These bonus features are not available in WebView based browsers or in Samsung Internet, because those browsers have no unified designs.

## Conclusion

ChromeXt fits an Android user who wants a userscript manager in a browser that has none, and who is willing to install an Xposed module or patch an APK to get it, particularly on Bromite, Brave or Samsung Internet where the scripts they rely on are otherwise unavailable. It does not fit a user unwilling to modify the browser package, since the non-root route is a patched APK that has to be reinstalled, and it does not fit someone who needs a stable, released build, since the last published release is from 2024 even though the code is still being pushed. Verify three things before you start: that your browser is one of the fully supported ones, that you know which Xposed variant you are installing, and that the scripts you plan to use work under the differences ChromeXt documents, particularly for @require without subresource integrity and for @sandbox, since the implementations are deliberately not identical to Tampermonkey's.

## FAQ

### What does the ChromeXt module do?

It adds UserScript and DevTools support to Chromium based and WebView based browsers on Android through the Xposed framework. The project hooks the onUpdateUrl function in UserScript.kt, compares the URL there, and evaluates JavaScript using the javascript: scheme or the DevTools Protocol when possible.

### Which browsers does ChromeXt support?

Bromite, Samsung Internet and Brave are fully supported, and most WebView based browsers work too, with the project asking for reports where they do not. Edge appears to have been dropped, since it is struck through in the list. For WebView based browsers you should enable the module only for the browser application you use, not for every WebView application and not for the Android system.

### Do I need root to install ChromeXt?

No. Root users install LSPosed first and then the latest built APK from the GitHub Actions. Non-root users download a modified LSPatch and the ChromeXt.apk from GitHub Actions, which requires being logged in to GitHub, and patch the browser with java -jar lspatch.jar arm64_ChromePublic.apk -d -v -m ChromeXt-signed.apk --force --injectdex, or use the provided manager APK with the Inject loader dex option.

### How do I enable the powerful GM.ChromeXt API?

It is locked by default so that users are protected from malicious UserScripts, and you unlock it per script by declaring @grant GM.ChromeXt. The API exposes the JavaScript method ChromeXt.dispatch(action, payload), and the dispatched action and payload are handled by Listener.kt, with usage examples in the GM.js asset.

### Where is the ChromeXt UserScript manager front end?

It is hosted on github.io at the project site, with two mirrors listed in case of connection issues, one on onrender.com and one on netlify.app. From the browser's three dots page menu, the Developer tools entry opens the manager, and the Eruda console entry gives you a console for other pages, with those items in the context menu for WebView based browsers and Samsung Internet.

## Sources

- [JingMatrix/ChromeXt on GitHub](https://github.com/JingMatrix/ChromeXt)
- [License: GPL-3.0](https://github.com/JingMatrix/ChromeXt/blob/master/LICENSE)
- [Project website](https://jingmatrix.github.io/ChromeXt/)
- [README](https://github.com/JingMatrix/ChromeXt/blob/master/README.md)
- [Releases](https://github.com/JingMatrix/ChromeXt/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/jingmatrix-chromext
