No-frills light-weight Android web browser with support for Greasemonkey userscripts.
Builds upon the WebView GM library demo application.
- the WebView GM library enhances the native Android System WebView
- with userscript management:
- detecting and downloading
*.user.jsURLs - parsing and saving to a DB
- automatic updates
- detecting and downloading
- with userscript injection:
- on top-level HTML pages that match URL patterns
- with support for Greasemonkey API ( 1, 2, 3 ) functions:
GM_addStyleGM_deleteValueGM_getResourceTextGM_getResourceURLGM_getValueGM_listValuesGM_logGM_setValueGM_xmlhttpRequest
- with userscript management:
- supplements the list of supported Greasemonkey API functions:
- legacy:
GM_addElementGM_cookie.deleteGM_cookie.listGM_cookie.setGM_download- where the
url(ordetails.url) parameter accepts any of the following data types:- String
- containing a valid URI having any of the following protocols:
- http:
- https:
- data:
- containing a valid URI having any of the following protocols:
- ArrayBuffer
- Uint8Array
- String
- only works on devices running API 19 (Android 4.4 KitKat) and higher
- requires SAF
- where the
GM_fetch- drop-in replacement for
window.fetchthat usesGM_xmlhttpRequestto make network requests
- drop-in replacement for
GM_infoGM_registerMenuCommandGM_unregisterMenuCommand
- GM 4:
GM.addElementGM.addStyleGM.cookie.deleteGM.cookie.listGM.cookie.setGM.cookies.deleteGM.cookies.listGM.cookies.setGM.deleteValueGM.downloadGM.fetchGM.getResourceTextGM.getResourceUrlGM.getValueGM.infoGM.listValuesGM.logGM.registerMenuCommandGM.setValueGM.unregisterMenuCommandGM.xmlHttpRequest
- legacy:
- adds an additional Javascript API interface to expose Android-specific capabilities:
- legacy:
GM_exit()- causes WebMonkey to close
GM_getUrl()- returns a String containing the URL that is currently loaded in the WebView
- use case:
- allows the userscript to detect whether the page has been redirected
- server response status codes: 301, 302
- allows the userscript to detect whether the page has been redirected
- example:
var is_redirect = (GM_getUrl() !== unsafeWindow.location.href)
GM_getUserAgent()- returns a String containing the User Agent that is currently configured in Settings for use by the WebView
GM_loadFrame(urlFrame, urlParent, proxyFrame)- loads an iframe into the WebView
- where:
- [required]
urlFrameis a String URL: the page loaded into the iframe - [required]
urlParentis a String URL: value forwindow.top.location.hrefandwindow.parent.location.hrefas observed from within the iframe - [optional]
proxyFrameis a boolean: a truthy value causesurlFrameto be downloaded in JavaurlParentis sent in the Referer header- a successful (200-299) response is dynamically loaded into iframe.srcdoc
- the benefit:
- same-origin policy does not apply
- when
urlParentandurlFramebelong to different domains, a userscript running in the top window can access the DOM within the iframe window
- special use case:
- when
urlFrameonly serves the desired web page content ifurlParentis sent in the Referer header
- when
- [required]
- example:
- use case:
- "parent_window.html" contains:
- an iframe to display "iframe_window.html"
- other content that is not wanted
- though a userscript could easily do the necessary housekeeping:
- detach the iframe
- remove all other DOM elements from body
- reattach the iframe
- this method provides a better solution:
- removes all scripts that are loaded into the parent window
- handles all the css needed to resize the iframe to maximize its display within the parent window
- makes it easy to handle this common case
- "parent_window.html" contains:
- why this is a common case:
- "iframe_window.html" performs a check to verify that it is loaded in the proper parent window
- example 1:
const urlParent = 'example.com/parent_window.html' try { // will throw when either: // - `top` is loaded from a different domain // - `top` is loaded from the same origin, but the URL path does not match 'parent_window.html' if(window.top.location.href !== urlParent) throw '' } catch(e) { // will redirect `top` window to the proper parent window window.top.location = urlParent }
- example 2:
const urlParent = 'example.com/parent_window.html' { // will redirect to proper parent window when 'iframe_window.html' is loaded without a `top` window if(window === window.top) window.location = urlParent }
GM_loadUrl(url, ...headers)- loads a URL into the WebView with additional HTTP request headers
- where:
- [required]
urlis a String URL - [optional]
headersis a list of String name/value pairs
- [required]
- example:
('example.com/iframe_window.html', 'Referer', 'example.com/parent_window.html')
GM_removeAllCookies()- completely removes all cookies for all web sites
GM_resolveUrl(urlRelative, urlBase)- returns a String containing
urlRelativeresolved relative tourlBase - where:
- [required]
urlRelativeis a String URL: relative path - [optional]
urlBaseis a String URL: absolute path- default value: the URL that is currently loaded in the WebView
- [required]
- examples:
('video.mp4', 'example.com/iframe_window.html')('video.mp4')
- returns a String containing
GM_setUserAgent(value)- changes the User Agent value that is configured in Settings
- where:
- [optional]
valueis a String- special cases:
WebView(or falsy)Chrome
- special cases:
- [optional]
GM_startIntent(action, data, type, ...extras)- starts an implicit Intent
- where:
- [required, can be empty]
actionis a String - [required, can be empty]
datais a String URL - [required, can be empty]
typeis a String mime-type for format ofdata - [optional]
extrasis a list of String name/value pairs
- [required, can be empty]
- example:
('android.intent.action.VIEW', 'example.com/video.mp4', 'video/mp4', 'referUrl', 'example.com/videos.html')
GM_toastLong(message)GM_toastShort(message)
- GM 4:
GM.exitGM.getUrlGM.getUserAgentGM.loadFrameGM.loadUrlGM.removeAllCookiesGM.resolveUrlGM.setUserAgentGM.startIntentGM.toastLongGM.toastShort
- legacy:
- default browser home page
- Continue where you left off
- Blank page
- Userscripts by developer
- Userscripts at Greasy Fork
- Custom URL
- User Agent
- WebView
- Chrome desktop
- Custom User Agent
- implementation for
@run-at document-end- document: DOMContentLoaded
- WebViewClient: onPageFinished
- this option can cause userscripts to run more than once per page load
- page load behavior on HTTPS certificate error
- cancel
- proceed
- ask
- script update interval
- number of days to wait between checks
- special case:
0disables automatic script updates
- shared secret for JS to access low-level API method:
window.WebViewWM.getUserscriptJS(secret, url)
- specific use case: mitmproxy script to inject JS code to bootstrap userscripts in iframes
- enable remote debugger
- allows remote access over an adb connection, such as:
adb connect "${IP_of_phone_on_LAN}:5555" - remote debugger is accessible in Chrome at:
chrome://inspect/#devices - the interface uses Chrome DevTools
- allows remote access over an adb connection, such as:
- enable AdBlock
- default: true
- custom Blocklist URL
- default: pgl.yoyo.org
- note: if this URL is empty or cannot be downloaded, a static copy of the default blocklist that is included in the app will be used
- custom Blocklist update interval
- number of days to wait before downloading a fresh copy of the blocklist
- default: 7
- closure
- by default, all JS code injected into web pages is wrapped by a closure
- the closure is implemented as a self-executing anonymous function, also known as an immediately invoked function expression
- this security feature can be disabled by a userscript by adding any of the following declarations to its header block:
// @unwrap // @flag noJsClosure // @flags noJsClosure - SANDBOX.txt contains more details
- sandbox
- when a closure is disabled, a sandbox is also disabled
- when a closure is enabled, by default, all JS global variables saved to the
windowObject are stored in a sandbox - as such, JS code outside of the userscript cannot see or access these variables
- however, the JS code inside of the userscript can see and access all global variables… including its own
- the sandbox is implemented as an ES6
Proxy - this security feature can be disabled by a userscript by adding any of the following declarations to its header block:
// @grant none // @flag noJsSandbox // @flags noJsSandbox - SANDBOX.txt contains more details
- API-level permissions
// @grant <API>is only required to use API methods that I would consider to be potentially dangerous- several of these API methods are grouped together,
and permission granted for any one…
also grants permission to use all other API methods in the same group- group:
GM_setValueGM_getValueGM_deleteValueGM_listValuesGM.setValueGM.getValueGM.deleteValueGM.listValues
- group:
GM_cookieGM_cookie.listGM_cookie.setGM_cookie.deleteGM.cookieGM.cookie.listGM.cookie.setGM.cookie.deleteGM.cookiesGM.cookies.listGM.cookies.setGM.cookies.delete
- group:
GM_removeAllCookiesGM.removeAllCookies
- group:
GM_setUserAgentGM.setUserAgent
- group:
- userscripts only run in the top window
- a mitmproxy script is required to load userscripts into iframes
- copyright: Warren Bank
- license: GPL-2.0





























