DotPageMod
Firefox WebExtension to load local CSS and JavaScript from your dotfiles into webpages
Install / Use
/learn @DonKult/DotPageModREADME
dotPageMod
Firefox WebExtension to load local CSS and JavaScript from your dotfiles into webpages.
Features
- each hostname has its own directory in your config directory of your own choosing
- the hostname directories itself can be stored in independent packs for easier sharing and syncing (e.g. private, public, work, home, …)
- port specific hosts with
hostname_port - hostnames like
mozilla.orgmatchdeveloper.mozilla.org - in fact:
orgmatches all hosts in that top-level domain, too ALLfor all hosts (on http/https) orALL_schemefor all hosts on a specific scheme- use
FRAMEWORKdirectory to include any/all JavaScript/CSS frameworks (like JQuery) you might want to use in your scripts – they are automatically included before your scripts are sourced. - scripts can show desktop notifications without WebAPI (see Cheatsheet)
- scripts are run in readyState
interactiveby default, but filenames ending in.start.jsrun scripts inloadingand.idle.jsatcompleteinstead (see also RunAt). - your JavaScript/CSS works even if the page has them blocked by a Content Security Policy or uMatrix
- your PageMods apply to the top window (not to frames) and apply to existing pages on (re)load
- on deactivation CSS sheets are automatically unapplied, JavaScript modifications can be reverted by registering an undo method
- on Linux with inotifywait (packaged in Debian in inotify-tools) the addon will reload automatically on relevant changes in the config directory
- a badge on the toolbar button indicates how many files modify the current tab. A list of these files can be accessed in the panel. A red badge & filename indicates a file couldn't be applied, e.g. due to a programming error in it.
(Better?) Alternatives
- Greasemonkey and various clones for all browsers
- dotjs for Chrome and dotjs for Firefox
- Stylish
- uBlock Origin with cosmetic filters
So, why another one?
A long while ago I was using Greasemonkey, but
just hiding a few elements with it felt very daunting with all this metadata
and editing in a browser window. I regressed to using Adblock
Plus and later uBlock
Origin for cosmetic filtering and more and
more to block the execution of JavaScript by default. Eventually I introduced
uMatrix with a rigid block-policy to the
mix which ended up leaving uBlock mostly jobless beside a bunch of cosmetic
filters. Management of these isn't perfect through and sometimes you want more
than just display: none – especially now that I had all of JavaScript blocked
by default and some websites downright refuse to be usable without it (e.g.
default collapsed infoboxes). So I moved my filters to ~/.css and started
fixing up websites in ~/.js with dotjs.
Quickly I ended up hitting issue #27: console.log doesn't work from
dotjs which I researched and
after commenting researched even more. Set out to write a patch to have this
option set automatically I ended up changing other things as well until I
realized that the architecture as-is wasn't to my liking (using a single global
PageMod reading files dynamically and sending the content to be processed by
eval (JS) and by DOM insertion (CSS) – the later failing in the event of a
content policy forbidding inline CSS) – and I always wanted to look into
developing Firefox extensions…
So, with a "how hard can it be?" I moved on to write my own extension to resolve my real as well as my imaginary problems by introducing new problems – not for me (hopefully), but potentially for anyone (else) wanting to use it…
Cheatsheet
path-specific CSS
@-moz-document url-prefix(http://www.w3.org/Style/) { }
@-moz-document regex("https:.*") { }
path-specific JavaScript
if (window.location.pathname === '/Style/')
if (window.location.pathname.startsWith('/Style/'))
undo changes
browser.runtime.onMessage.addListener(l => {
if (l.cmd !== 'detach') return;
// TODO: react here
});
Note: The example nano-framework has some wrappers and examples to undo common changes like event handlers, style toggles and removal of added elements.
Note: If a CSS file is changed and config reloaded the old CSS will be cleanly removed from open tabs and the new one applied. The situation is more complicated for JS: Individual files can't be unapplied (obviously), so in this case the tab gets the detach message described above and all JS files effecting this tab are reapplied. If the scripts effecting this tab have no clean detach behaviour you might be better off reloading the page to get a fresh start.
Beware: This doesn't work on addon update/removal as there is no API for it. The closest might be onSuspend but it is neither implemented in Firefox nor a good drop-in as it can still be cancelled and comes with no runtime guarantees whatsoever. I really hope browser vendors will make up their mind on this as this doesn't feel like a good user experience. On the upside, you hopefully don't need to update the extension itself all too often – and of course never need to remove it. ;)
showing desktop notifications
browser.runtime.sendMessage({'cmd': 'notify', 'title': title, 'message': msg});
Note: A notification via WebAPI requires the website to have permission for it, but our scripts are written by the user, so permission is implicitly given – and independent from the permission status of the website.
modifying current tab (pin, active, mute, reload, close, …)
browser.runtime.sendMessage('tab/activate');
browser.runtime.sendMessage('tab/pin');
browser.runtime.sendMessage('tab/unpin');
browser.runtime.sendMessage('tab/mute');
browser.runtime.sendMessage('tab/unmute');
browser.runtime.sendMessage('tab/close');
browser.runtime.sendMessage('tab/reload');
browser.runtime.sendMessage('tab/force-reload');
window.location = 'https://example.org';
browser.runtime.sendMessage({ cmd: 'tab/url', url: 'https://example.org'});
open a new tab
window.open(url, '_blank');
browser.runtime.sendMessage({ cmd: 'tab/open', active: true, pinned: false, window: 'tab', url: 'https://example.org' });
Note: The first option requires the website to have popup permissions. The second is less simple, but can open tabs in the 'current' window or in the window the tab belongs to the script runs in (default).
embedding images and co in CSS
SVG
URL encoding is enough here and has the benefit of allowing modifications still,
the encoded string can be used as url(data:image/svg+xml,ENCODED).
perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "to encode"
echo 'to decode' | awk -niord '{printf RT?$0chr("0x"substr(RT,2)):$0}' RS=%..
(Source for encode and decode commands)
binary image formats and other things
The URI needs to be url(data:image/TYPE;base64,ENCODED).
Encoding can be done with base64 -w 0 < image.file.
URL to directory resolution
http://dotpagemod.example.com:8080/=>ALL,ALL_http,com,com_8080,example.com,example.com_8080,dotpagemod.example.com,dotpagemod.example.com_8080http://dotpagemod.example.com/=>ALL,ALL_http,com,com_80,example.com,example.com_80,dotpagemod.example.com,dotpagemod.example.com_80https://dotpagemod.example.com/=>ALL,ALL_https,com,com_443,example.com,example.com_443,dotpagemod.example.com,dotpagemod.example.com_443ftp://dotpagemod.example.com/=>ALL_ftp,com,com_21,example.com,example.com_21,dotpagemod.example.com,dotpagemod.example.com_21
Examples
Websites like OpenUserJS, Greasy
Fork and UserStyles can be
an inspiration of what you could possibly do with your new JS/CSS powers given
enough dedication. More (humble) examples can be easily found e.g. on github by
searching for dotjs.
If you are more interested in seeing an example of how this extension could be used in practice by a user you can have a look at the examples folder.
Note: The examples are provided as-is, I am neither endorsing nor recommending using any of the examples or even the websites they might apply to even if they might have been used by me at some point in the past. No guarantees are made that they work as intended and/or (not) eat your babies (instead). You have been warned.
Website owners who find examples applying to their sites should remember that obviously a user has cared deeply enough about the site to modify it to cater even better to its own needs instead of moving away to a competitor before starting a "you are experiencing it all wrong – professionals have designed that for you!" frenzy. You might even recognize the chance to
