mirror of
https://github.com/vale981/tridactyl
synced 2025-03-05 17:41:40 -05:00
Merge branch 'master' of github.com:cmcaine/tridactyl into hint
This commit is contained in:
commit
74117e29ee
14 changed files with 433 additions and 128 deletions
78
CHANGELOG.md
78
CHANGELOG.md
|
@ -2,82 +2,82 @@
|
|||
|
||||
## Release 1.13.2 / Unreleased
|
||||
|
||||
* New features:
|
||||
* New features:
|
||||
|
||||
* Rapid hinting bound to `gF`. One of our most requested features.
|
||||
* Rapid hinting bound to `gF`. One of our most requested features.
|
||||
|
||||
* Add `DocLoad` autocmd which triggers after all HTML is downloaded (it fires on DOMContentLoaded).
|
||||
* Add `DocLoad` autocmd which triggers after all HTML is downloaded (it fires on DOMContentLoaded).
|
||||
|
||||
* All clipboard commands on Linux now support X-selection if you have the native messenger installed. Simply set `yankto` and `putfrom` to `selection`.
|
||||
* All clipboard commands on Linux now support X-selection if you have the native messenger installed. Simply set `yankto` and `putfrom` to `selection`.
|
||||
|
||||
* Add private window indicator to `bufferall`; add container icons to `buffer{,all}`.
|
||||
* Add private window indicator to `bufferall`; add container icons to `buffer{,all}`.
|
||||
|
||||
* Add `fillcmdline_tmp`, useful for temporary messages. A few commands now use this (e.g, `yy`).
|
||||
* Add `fillcmdline_tmp`, useful for temporary messages. A few commands now use this (e.g, `yy`).
|
||||
|
||||
* `tabmove` bound to `<<` and `>>` à la Vimium.
|
||||
* `tabmove` bound to `<<` and `>>` à la Vimium.
|
||||
|
||||
* Add `mute` to mute tabs. Bound to `<A-m>` by default.
|
||||
* Add `mute` to mute tabs. Bound to `<A-m>` by default.
|
||||
|
||||
* Add `<A-p>` bind for `pin`.
|
||||
* Add `<A-p>` bind for `pin`.
|
||||
|
||||
* Add `{fixamo,guiset}_quiet` for non-interactive use; normal `{fixamo,guiset}` now inform you that you must restart.
|
||||
* Add `{fixamo,guiset}_quiet` for non-interactive use; normal `{fixamo,guiset}` now inform you that you must restart.
|
||||
|
||||
* Add `url2args` ex-command to retrieve search terms from Tridactyl search engines, for use with `O`. `help url2args` for more information.
|
||||
* Add `url2args` ex-command to retrieve search terms from Tridactyl search engines, for use with `O`. `help url2args` for more information.
|
||||
|
||||
* Add `autocmddelete` to delete an autocmd.
|
||||
* Add `autocmddelete` to delete an autocmd.
|
||||
|
||||
* Add binds for yankmd and yanktitle to `yt` and `ym` irrespectively.
|
||||
* Add binds for yankmd and yanktitle to `yt` and `ym` irrespectively.
|
||||
|
||||
* Our GitHub has a new troublehooting guide and issue template (#522).
|
||||
* Our GitHub has a new troublehooting guide and issue template (#522).
|
||||
|
||||
* Bug fixes:
|
||||
* Bug fixes:
|
||||
|
||||
* Fix race condition in state.mode synchronization (#613).
|
||||
* Fix race condition in state.mode synchronization (#613).
|
||||
|
||||
* `set newtab about:blank` should work once again (#678).
|
||||
* `set newtab about:blank` should work once again (#678).
|
||||
|
||||
* Make `tabprev` synchronous: it now works better in `composite` commands (i.e, `D` is less janky).
|
||||
* Make `tabprev` synchronous: it now works better in `composite` commands (i.e, `D` is less janky).
|
||||
|
||||
* Fix `guiset hoverlink *` in Firefox 61 (#763).
|
||||
* Fix `guiset hoverlink *` in Firefox 61 (#763).
|
||||
|
||||
* Make `source` ignore visually empty lines.
|
||||
* Make `source` ignore visually empty lines.
|
||||
|
||||
* Completions will now be properly deselected upon typing (#833).
|
||||
* Completions will now be properly deselected upon typing (#833).
|
||||
|
||||
* `guiset` now gives helpful errors if given the wrong arguments (#844).
|
||||
* `guiset` now gives helpful errors if given the wrong arguments (#844).
|
||||
|
||||
* History completion insertion with space no longer inserts an extra space (#838).
|
||||
* History completion insertion with space no longer inserts an extra space (#838).
|
||||
|
||||
* Ctrl-y actually scrolls up now
|
||||
* Ctrl-y actually scrolls up now
|
||||
|
||||
* Arguments now ignored on history completions (`-private, -c, -b` etc.).
|
||||
* Arguments now ignored on history completions (`-private, -c, -b` etc.).
|
||||
|
||||
* Native messenger:
|
||||
* Native messenger:
|
||||
|
||||
* Windows install script now complains if you do not have the requisite PowerShell version.
|
||||
* Windows install script now complains if you do not have the requisite PowerShell version.
|
||||
|
||||
* Windows install script should now work if you have a non-ASCII username/directory
|
||||
* Windows install script should now work if you have a non-ASCII username/directory
|
||||
|
||||
* Windows install script no longer rage-quits if Python is not found.
|
||||
* This means that the compiled executable will actually be used. It's much slower than the normal Python script, so we strongly recommend that you use that instead by installing Python 3, making sure it is on your PATH, and running `installnative` again.
|
||||
* Windows install script no longer rage-quits if Python is not found.
|
||||
* This means that the compiled executable will actually be used. It's much slower than the normal Python script, so we strongly recommend that you use that instead by installing Python 3, making sure it is on your PATH, and running `installnative` again.
|
||||
|
||||
* Fix focus hijacking again (#768).
|
||||
* Fix focus hijacking again (#768).
|
||||
|
||||
* Fix scrolling on bugzilla.mozilla.org (#762).
|
||||
* Fix scrolling on bugzilla.mozilla.org (#762).
|
||||
|
||||
* Fix race condition in :sanitise (#724).
|
||||
* Fix race condition in :sanitise (#724).
|
||||
|
||||
* Make sure bind/unbind use the same binding format: previously, modifiers on binds were case-sensitive for some commands.
|
||||
* Make sure bind/unbind use the same binding format: previously, modifiers on binds were case-sensitive for some commands.
|
||||
|
||||
* Container commands are now more case-insensitive.
|
||||
* Container commands are now more case-insensitive.
|
||||
|
||||
* Fix jumplist not being correctly restored on reloads (#680).
|
||||
* Fix jumplist not being correctly restored on reloads (#680).
|
||||
|
||||
* Update 1.13.1 release date in time for 1.13.2
|
||||
* Update 1.13.1 release date in time for 1.13.2
|
||||
|
||||
* Boring internal stuff
|
||||
* Boring internal stuff
|
||||
|
||||
* Move most of hinting to content script (this may have broken some stuff - please report it if it has).
|
||||
* Move most of hinting to content script (this may have broken some stuff - please report it if it has).
|
||||
|
||||
Thanks to all of our contributors for this release: Oliver Blanthorn, glacambre, Anton Vilhelm Ásgeirsson, Babil Golam Sarwar, Jeff King, Bzly, WorldCodeCentral, Colin Caine, Vladimir Macko, Bodo Graumann, Chris Pickard, Matt Friedman, Susexe, jcrowgey.
|
||||
|
||||
|
|
|
@ -18,11 +18,7 @@ If that does not solve your problem, please fill in the following template and t
|
|||
|
||||
* Steps to reproduce:
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
5.
|
||||
1. 2. 3. 4. 5.
|
||||
|
||||
* Tridactyl version (`:version`):
|
||||
|
||||
|
@ -41,5 +37,5 @@ Insert tridactylrc contents between the backticks
|
|||
<!-- If your bug is about Tridactyl's native executable, please add the following information: -->
|
||||
|
||||
* Operating system:
|
||||
* Result of running `:! echo $PATH` in tridactyl:
|
||||
* Result of running `:! echo $PATH`, or `! echo %PATH%` on Windows, in Tridactyl:
|
||||
* Unix-like only: result of running `printf '%c\0\0\0{"cmd": "run", "command": "echo $PATH"}' 39 | ~/.local/share/tridactyl/native_main.py` in a terminal:
|
||||
|
|
|
@ -14,7 +14,7 @@ import time
|
|||
import unicodedata
|
||||
|
||||
DEBUG = False
|
||||
VERSION = "0.1.7"
|
||||
VERSION = "0.1.8"
|
||||
|
||||
|
||||
class NoConnectionError(Exception):
|
||||
|
@ -95,6 +95,8 @@ def findUserConfigFile():
|
|||
candidate_files = [
|
||||
os.path.join(config_dir, "tridactyl", "tridactylrc"),
|
||||
os.path.join(home, ".tridactylrc"),
|
||||
os.path.join(home, "_config", "tridactyl", "tridactylrc"),
|
||||
os.path.join(home, "_tridactylrc"),
|
||||
]
|
||||
|
||||
config_path = None
|
||||
|
|
|
@ -31,6 +31,7 @@ import * as native from "./native_background"
|
|||
import * as msgsafe from "./msgsafe"
|
||||
import state from "./state"
|
||||
import * as webext from "./lib/webext"
|
||||
import { AutoContain } from "./lib/autocontainers"
|
||||
;(window as any).tri = Object.assign(Object.create(null), {
|
||||
messaging,
|
||||
excmds,
|
||||
|
@ -110,3 +111,34 @@ browser.tabs.onActivated.addListener(ev => {
|
|||
curTab = ev.tabId
|
||||
messaging.messageTab(curTab, "excmd_content", "loadaucmds", ["TabEnter"])
|
||||
})
|
||||
|
||||
// {{{ AUTOCONTAINERS
|
||||
|
||||
let aucon = new AutoContain()
|
||||
|
||||
// Handle cancelled requests as a result of autocontain.
|
||||
browser.webRequest.onCompleted.addListener(
|
||||
details => {
|
||||
if (aucon.getCancelledRequest(details.tabId)) {
|
||||
aucon.clearCancelledRequests(details.tabId)
|
||||
}
|
||||
},
|
||||
{ urls: ["<all_urls"], types: ["main_frame"] },
|
||||
)
|
||||
|
||||
browser.webRequest.onErrorOccurred.addListener(
|
||||
details => {
|
||||
if (aucon.getCancelledRequest(details.tabId)) {
|
||||
aucon.clearCancelledRequests(details.tabId)
|
||||
}
|
||||
},
|
||||
{ urls: ["<all_urls>"], types: ["main_frame"] },
|
||||
)
|
||||
|
||||
// Contain autocmd.
|
||||
browser.webRequest.onBeforeRequest.addListener(
|
||||
aucon.autoContain,
|
||||
{ urls: ["<all_urls>"], types: ["main_frame"] },
|
||||
["blocking"],
|
||||
)
|
||||
// }}}
|
||||
|
|
|
@ -41,13 +41,22 @@ const DEFAULTS = o({
|
|||
}),
|
||||
inputmaps: o({
|
||||
"<Esc>": "composite unfocus | mode normal",
|
||||
"<C-[>": "composite unfocus | mode normal",
|
||||
"<C-i>": "editor",
|
||||
"<Tab>": "focusinput -n",
|
||||
"<S-Tab>": "focusinput -N",
|
||||
"<CA-Esc>": "mode normal",
|
||||
"<CA-`>": "mode normal",
|
||||
"<C-^>": "buffer #",
|
||||
}),
|
||||
imaps: o({
|
||||
"<Esc>": "composite unfocus | mode normal",
|
||||
"<C-[>": "composite unfocus | mode normal",
|
||||
"<C-i>": "editor",
|
||||
"<CA-Esc>": "mode normal",
|
||||
"<CA-`>": "mode normal",
|
||||
"<C-6>": "buffer #",
|
||||
"<C-^>": "buffer #",
|
||||
}),
|
||||
nmaps: o({
|
||||
"<F1>": "help",
|
||||
|
@ -91,6 +100,7 @@ const DEFAULTS = o({
|
|||
// "0": "scrollto 0 x", // will get interpreted as a count
|
||||
"^": "scrollto 0 x",
|
||||
"<C-6>": "buffer #",
|
||||
"<C-^>": "buffer #",
|
||||
H: "back",
|
||||
L: "forward",
|
||||
"<C-o>": "jumpprev",
|
||||
|
@ -151,8 +161,9 @@ const DEFAULTS = o({
|
|||
"<CA-Esc>": "mode ignore",
|
||||
"<CA-`>": "mode ignore",
|
||||
"<Esc>": "composite mode normal ; hidecmdline",
|
||||
"<C-[>": "composite mode normal ; hidecmdline",
|
||||
I:
|
||||
"fillcmdline Ignore mode is now toggled by pressing <S-Insert> or <C-A-`>",
|
||||
"fillcmdline_tmp 3000 Ignore mode is now toggled by pressing <S-Insert> or <C-A-`>",
|
||||
a: "current_url bmark",
|
||||
A: "bmark",
|
||||
zi: "zoom 0.1 true",
|
||||
|
@ -184,9 +195,14 @@ const DEFAULTS = o({
|
|||
// "emacs.org": "tabclose",
|
||||
}),
|
||||
}),
|
||||
autocontain: o({
|
||||
//"github.com": "microsoft",
|
||||
//"youtube.com": "google",
|
||||
}),
|
||||
exaliases: o({
|
||||
alias: "command",
|
||||
au: "autocmd",
|
||||
aucon: "autocontain",
|
||||
audel: "autocmddelete",
|
||||
audelete: "autocmddelete",
|
||||
b: "buffer",
|
||||
|
@ -220,13 +236,15 @@ const DEFAULTS = o({
|
|||
openwith: "hint -W",
|
||||
"!": "exclaim",
|
||||
"!s": "exclaim_quiet",
|
||||
containerremove: "containerdelete",
|
||||
colourscheme: "set theme",
|
||||
colours: "colourscheme",
|
||||
colorscheme: "colourscheme",
|
||||
colors: "colourscheme",
|
||||
man: "help",
|
||||
"!js": "js",
|
||||
"!jsb": "jsb",
|
||||
"!js": "fillcmdline_tmp 3000 !js is deprecated. Please use js instead",
|
||||
"!jsb":
|
||||
"fillcmdline_tmp 3000 !jsb is deprecated. Please use jsb instead",
|
||||
current_url: "composite get_current_url | fillcmdline_notrail ",
|
||||
}),
|
||||
followpagepatterns: o({
|
||||
|
@ -331,12 +349,16 @@ const DEFAULTS = o({
|
|||
profiledir: "auto",
|
||||
|
||||
// Container settings
|
||||
|
||||
// If enabled, tabopen opens a new tab in the currently active tab's container.
|
||||
tabopencontaineraware: "false",
|
||||
|
||||
// If moodeindicator is enabled, containerindicator will color the border of the mode indicator with the container color.
|
||||
containerindicator: "true",
|
||||
|
||||
// Autocontain directives create a container if it doesn't exist already.
|
||||
auconcreatecontainer: "true",
|
||||
|
||||
// Performance related settings
|
||||
|
||||
// number of most recent results to ask Firefox for. We display the top 20 or so most frequently visited ones.
|
||||
|
|
|
@ -287,7 +287,7 @@ export async function guiset_quiet(rule: string, option: string) {
|
|||
//#background
|
||||
export async function guiset(rule: string, option: string) {
|
||||
await guiset_quiet(rule, option)
|
||||
fillcmdline_tmp("3000", "userChrome.css written. Please restart Firefox to see the changes.")
|
||||
fillcmdline_tmp(3000, "userChrome.css written. Please restart Firefox to see the changes.")
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
|
@ -319,7 +319,7 @@ export async function fixamo_quiet() {
|
|||
//#background
|
||||
export async function fixamo() {
|
||||
await fixamo_quiet()
|
||||
fillcmdline_tmp("3000", "Permissions added to user.js. Please restart Firefox to make them take affect.")
|
||||
fillcmdline_tmp(3000, "Permissions added to user.js. Please restart Firefox to make them take affect.")
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -415,7 +415,9 @@ export async function installnative() {
|
|||
/**
|
||||
* Runs an RC file from disk.
|
||||
*
|
||||
* If no argument given, it will try to open ~/.tridactylrc, ~/.config/tridactylrc or $XDG_CONFIG_HOME/tridactyl/tridactylrc in reverse order.
|
||||
* If no argument given, it will try to open ~/.tridactylrc, ~/.config/tridactylrc or $XDG_CONFIG_HOME/tridactyl/tridactylrc in reverse order. You may use a `_` in place of a leading `.` if you wish, e.g, if you use Windows.
|
||||
*
|
||||
* On Windows, the `~` expands to `%USERPROFILE%`.
|
||||
*
|
||||
* The RC file is just a bunch of Tridactyl excmds (i.e, the stuff on this help page). Settings persist in local storage; add `sanitise tridactyllocal tridactylsync` to make it more Vim like. There's an [example file](https://raw.githubusercontent.com/cmcaine/tridactyl/master/.tridactylrc) if you want it.
|
||||
*
|
||||
|
@ -1787,14 +1789,28 @@ export async function undo() {
|
|||
@param index
|
||||
New index for the current tab.
|
||||
|
||||
1 is the first index. 0 is the last index. -1 is the penultimate, etc.
|
||||
1,start,^ are aliases for the first index. 0,end,$ are aliases for the last index.
|
||||
*/
|
||||
//#background
|
||||
export async function tabmove(index = "0") {
|
||||
const aTab = await activeTab()
|
||||
const maxindex = (await browser.tabs.query({ currentWindow: true })).length - 1
|
||||
let newindex: number
|
||||
if (index.startsWith("+") || index.startsWith("-")) {
|
||||
newindex = Math.max(0, Number(index) + aTab.index)
|
||||
|
||||
if (index.startsWith("+")) {
|
||||
newindex = Number(index) + aTab.index
|
||||
if (newindex > maxindex) {
|
||||
newindex -= maxindex + 1
|
||||
}
|
||||
} else if (index.startsWith("-")) {
|
||||
newindex = Number(index) + aTab.index
|
||||
if (newindex < 0) {
|
||||
newindex += maxindex + 1
|
||||
}
|
||||
} else if (["end", "$"].includes(index)) {
|
||||
newindex = maxindex
|
||||
} else if (["start", "^"].includes(index)) {
|
||||
newindex = 0
|
||||
} else newindex = Number(index) - 1
|
||||
browser.tabs.move(aTab.id, { index: newindex })
|
||||
}
|
||||
|
@ -1942,7 +1958,7 @@ export async function containercreate(name: string, color?: string, icon?: strin
|
|||
@param name The container name.
|
||||
*/
|
||||
//#background
|
||||
export async function containerremove(name: string) {
|
||||
export async function containerdelete(name: string) {
|
||||
await containerclose(name)
|
||||
await Container.remove(name)
|
||||
}
|
||||
|
@ -1990,6 +2006,7 @@ export async function viewcontainers() {
|
|||
.replace(/#/g, "%23")
|
||||
.replace(/ /g, "%20")
|
||||
}
|
||||
|
||||
// }}}
|
||||
//
|
||||
// {{{ MISC
|
||||
|
@ -2159,8 +2176,7 @@ export function fillcmdline_nofocus(...strarr: string[]) {
|
|||
|
||||
/** Shows str in the command line for ms milliseconds. Recommended duration: 3000ms. */
|
||||
//#background
|
||||
export async function fillcmdline_tmp(ms: string, ...strarr: string[]) {
|
||||
let milliseconds = parseInt(ms)
|
||||
export async function fillcmdline_tmp(ms: number, ...strarr: string[]) {
|
||||
let str = strarr.join(" ")
|
||||
let tabId = await activeTabId()
|
||||
showcmdline(false)
|
||||
|
@ -2172,7 +2188,7 @@ export async function fillcmdline_tmp(ms: string, ...strarr: string[]) {
|
|||
await messageTab(tabId, "commandline_frame", "clear", [true])
|
||||
}
|
||||
resolve()
|
||||
}, milliseconds),
|
||||
}, ms),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -2270,7 +2286,7 @@ export async function clipboard(excmd: "open" | "yank" | "yankshort" | "yankcano
|
|||
}
|
||||
if (urls.length > 0) {
|
||||
await yank(urls[0])
|
||||
fillcmdline_tmp("3000", "# " + urls[0] + " copied to clipboard.")
|
||||
fillcmdline_tmp(3000, "# " + urls[0] + " copied to clipboard.")
|
||||
break
|
||||
}
|
||||
// Trying yankcanon if yankshort failed...
|
||||
|
@ -2278,24 +2294,24 @@ export async function clipboard(excmd: "open" | "yank" | "yankshort" | "yankcano
|
|||
urls = await geturlsforlinks("rel", "canonical")
|
||||
if (urls.length > 0) {
|
||||
await yank(urls[0])
|
||||
fillcmdline_tmp("3000", "# " + urls[0] + " copied to clipboard.")
|
||||
fillcmdline_tmp(3000, "# " + urls[0] + " copied to clipboard.")
|
||||
break
|
||||
}
|
||||
// Trying yank if yankcanon failed...
|
||||
case "yank":
|
||||
content = content == "" ? (await activeTab()).url : content
|
||||
await yank(content)
|
||||
fillcmdline_tmp("3000", "# " + content + " copied to clipboard.")
|
||||
fillcmdline_tmp(3000, "# " + content + " copied to clipboard.")
|
||||
break
|
||||
case "yanktitle":
|
||||
content = (await activeTab()).title
|
||||
await yank(content)
|
||||
fillcmdline_tmp("3000", "# " + content + " copied to clipboard.")
|
||||
fillcmdline_tmp(3000, "# " + content + " copied to clipboard.")
|
||||
break
|
||||
case "yankmd":
|
||||
content = "[" + (await activeTab()).title + "](" + (await activeTab()).url + ")"
|
||||
await yank(content)
|
||||
fillcmdline_tmp("3000", "# " + content + " copied to clipboard.")
|
||||
fillcmdline_tmp(3000, "# " + content + " copied to clipboard.")
|
||||
break
|
||||
case "open":
|
||||
url = await getclip()
|
||||
|
@ -2427,23 +2443,34 @@ export function comclear(name: string) {
|
|||
[[fillcmdline]] to put a string in the cmdline and focus the cmdline
|
||||
(otherwise the string is executed immediately).
|
||||
|
||||
|
||||
You can bind to other modes with `bind --mode={insert|ignore|normal|input} ...`, e.g, `bind --mode=insert emacs qall` (NB: unlike vim, all preceeding characters will not be input).
|
||||
|
||||
See also:
|
||||
|
||||
- [[unbind]]
|
||||
- [[reset]]
|
||||
*/
|
||||
//#background
|
||||
export function bind(key: string, ...bindarr: string[]) {
|
||||
export function bind(...args: string[]) {
|
||||
let mode = "normal"
|
||||
let mode2maps = new Map([["normal", "nmaps"], ["ignore", "ignoremaps"], ["insert", "imaps"], ["input", "inputmaps"]])
|
||||
if (args[0].startsWith("--mode=")) {
|
||||
mode = args.shift().replace("--mode=", "")
|
||||
}
|
||||
if (!mode2maps.has(mode)) fillcmdline("Mode " + mode + " does not yet have user-configurable binds.")
|
||||
let key = args.shift()
|
||||
let bindarr = args
|
||||
// Convert key to internal representation
|
||||
key = mapstrToKeyseq(key)
|
||||
.map(k => k.toMapstr())
|
||||
.join("")
|
||||
if (bindarr.length) {
|
||||
let exstring = bindarr.join(" ")
|
||||
config.set("nmaps", key, exstring)
|
||||
config.set(mode2maps.get(mode), key, exstring)
|
||||
} else if (key.length) {
|
||||
// Display the existing bind
|
||||
fillcmdline_notrail("#", key, "=", config.get("nmaps", key))
|
||||
fillcmdline_notrail("#", key, "=", config.get(mode2maps.get(mode), key))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2539,6 +2566,25 @@ export function autocmd(event: string, url: string, ...excmd: string[]) {
|
|||
config.set("autocmds", event, url, excmd.join(" "))
|
||||
}
|
||||
|
||||
/** Automatically open a domain and all its subdomains in a specified container.
|
||||
*
|
||||
* For declaring containers that do not yet exist, consider using `auconscreatecontainer true` in your tridactylrc.
|
||||
* This allows tridactyl to automatically create containers from your autocontain directives. Note that they will be random icons and colors.
|
||||
*
|
||||
* ** NB: This is an experimental feature, if you encounter issues please create an issue on github. **
|
||||
*
|
||||
* The domain is passed through as a regular expression so there are a few gotchas to be aware of:
|
||||
* * Unescaped periods will match *anything*. `autocontain google.co.uk work` will match `google!co$uk`. Escape your periods or accept that you might get some false positives.
|
||||
* * You can use regex in your domain pattern. `autocontain google\,(co\.uk|com) work` will match either `google.co.uk` or `google.com`.
|
||||
*
|
||||
* @param domain The domain which will trigger the autoContain directive. Includes all subdomains.
|
||||
* @param container The container to open the url in.
|
||||
*/
|
||||
//#background
|
||||
export function autocontain(domain: string, container: string) {
|
||||
config.set("autocontain", domain, container)
|
||||
}
|
||||
|
||||
/** Remove autocmds
|
||||
@param event Curently, 'TriStart', 'DocStart', 'DocLoad', 'DocEnd', 'TabEnter' and 'TabLeft' are supported.
|
||||
|
||||
|
@ -3106,8 +3152,6 @@ export async function echo(...str: string[]) {
|
|||
*
|
||||
* Some of Tridactyl's functions are accessible here via the `tri` object. Just do `console.log(tri)` in the web console on the new tab page to see what's available.
|
||||
*
|
||||
* Aliased to `!js`
|
||||
*
|
||||
* If you want to pipe an argument to `js`, you need to use the "-p" flag and then use the JS_ARG global variable, e.g:
|
||||
*
|
||||
* `composite get_current_url | js -p alert(JS_ARG)`
|
||||
|
|
|
@ -12,7 +12,6 @@ async function reset() {
|
|||
return await messageActiveTab("hinting_content", "reset")
|
||||
}
|
||||
|
||||
|
||||
/** Type for "hint save" actions:
|
||||
* - "link": elements that point to another resource (eg
|
||||
* links to pages/files) - the link target is saved
|
||||
|
|
|
@ -25,8 +25,25 @@ import state from "./state"
|
|||
|
||||
import * as generic from "./parsers/genericmode"
|
||||
let normparser = keys => generic.parser("nmaps", keys)
|
||||
let inputparser = keys => generic.parser("inputmaps", keys)
|
||||
let insertparser = keys => generic.parser("imaps", keys)
|
||||
let ignoreparser = keys => generic.parser("ignoremaps", keys)
|
||||
let keys = []
|
||||
|
||||
function genericResponse(ke: KeyboardEvent, parser: any): void {
|
||||
keys.push(ke)
|
||||
const response = parser(keys)
|
||||
|
||||
// Suppress if there's a match.
|
||||
if (response.isMatch) {
|
||||
ke.preventDefault()
|
||||
ke.stopImmediatePropagation()
|
||||
}
|
||||
|
||||
// Update keys array.
|
||||
keys = response.keys || []
|
||||
}
|
||||
|
||||
/** Choose to suppress a key or not */
|
||||
function modeSpecificSuppression(ke: KeyboardEvent) {
|
||||
let mode = state.mode
|
||||
|
@ -53,17 +70,16 @@ function modeSpecificSuppression(ke: KeyboardEvent) {
|
|||
|
||||
switch (mode) {
|
||||
case "normal":
|
||||
keys.push(ke)
|
||||
const response = normparser(keys)
|
||||
|
||||
// Suppress if there's a match.
|
||||
if (response.isMatch) {
|
||||
ke.preventDefault()
|
||||
ke.stopImmediatePropagation()
|
||||
}
|
||||
|
||||
// Update keys array.
|
||||
keys = response.keys || []
|
||||
genericResponse(ke, normparser)
|
||||
break
|
||||
case "input":
|
||||
genericResponse(ke, inputparser)
|
||||
break
|
||||
case "insert":
|
||||
genericResponse(ke, insertparser)
|
||||
break
|
||||
case "ignore":
|
||||
genericResponse(ke, ignoreparser)
|
||||
break
|
||||
// Hintmode can't clean up after itself yet, so it needs to block more FF shortcuts.
|
||||
case "hint":
|
||||
|
@ -79,20 +95,6 @@ function modeSpecificSuppression(ke: KeyboardEvent) {
|
|||
ke.stopImmediatePropagation()
|
||||
}
|
||||
break
|
||||
case "input":
|
||||
if (ke.key === "Tab" || (ke.ctrlKey === true && ke.key === "i")) {
|
||||
ke.preventDefault()
|
||||
ke.stopImmediatePropagation()
|
||||
}
|
||||
break
|
||||
case "ignore":
|
||||
break
|
||||
case "insert":
|
||||
if (ke.ctrlKey === true && ke.key === "i") {
|
||||
ke.preventDefault()
|
||||
ke.stopImmediatePropagation()
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
|
186
src/lib/autocontainers.ts
Normal file
186
src/lib/autocontainers.ts
Normal file
|
@ -0,0 +1,186 @@
|
|||
/** Auto Containers
|
||||
|
||||
Hook into webRequests and make sure that your (least) favorite domain is contained
|
||||
and doesn't touch your default browsing environment.
|
||||
|
||||
For declaring containers that do not yet exist, consider using `auconscreatecontainer true` in your tridactylrc.
|
||||
This allows tridactyl to automatically create containers from your autocontain directives. Note that they will be random icons and colors.
|
||||
|
||||
** NB: This is an experimental feature, if you encounter issues please create an issue on github. **
|
||||
|
||||
The domain is passed through as a regular expression so there are a few gotchas to be aware of:
|
||||
* Unescaped periods will match *anything*. `autocontain google.co.uk work` will match `google!co$uk`. Escape your periods or accept that you might get some false positives.
|
||||
* You can use regex in your domain pattern. `autocontain google\,(co\.uk|com) work` will match either `google.co.uk` or `google.com`.
|
||||
|
||||
TODO: Should try and detect Multi Account Containers and/or Contain Facebook extensions from Mozilla.
|
||||
|
||||
A lot of the inspiration for this code was drawn from the Mozilla `contain facebook` Extension.
|
||||
https://github.com/mozilla/contain-facebook/
|
||||
|
||||
This feature is experimental and may cause heavy CPU usage.
|
||||
|
||||
Issue in MAC that is seemingly caused by the same thing:
|
||||
https://github.com/mozilla/multi-account-containers/issues/572
|
||||
|
||||
*/
|
||||
|
||||
import * as Config from "../config"
|
||||
import * as Container from "./containers"
|
||||
import * as Logging from "../logging"
|
||||
|
||||
const logger = new Logging.Logger("containers")
|
||||
|
||||
/** An interface for the additional object that's supplied in the BlockingResponse callback.
|
||||
|
||||
Details here:
|
||||
https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/webRequest/onBeforeRequest#details
|
||||
|
||||
*/
|
||||
interface IDetails {
|
||||
frameAncestors: any[]
|
||||
frameId: number
|
||||
method: string
|
||||
originUrl: string
|
||||
parentFrameId: number
|
||||
proxyInfo?: any
|
||||
requestBody?: any
|
||||
requestId: string
|
||||
tabId: number
|
||||
timeStamp: number
|
||||
type: browser.webRequest.ResourceType
|
||||
url: string
|
||||
}
|
||||
|
||||
interface ICancelledRequest {
|
||||
requestIds: any
|
||||
urls: any
|
||||
}
|
||||
|
||||
interface IAutoContain {
|
||||
autoContain(details: IDetails): any
|
||||
cancelEarly(tab: browser.tabs.Tab, details: IDetails): boolean
|
||||
cancelRequest(tab: browser.tabs.Tab, details: IDetails): void
|
||||
clearCancelledRequests(tabId: number): void
|
||||
getCancelledRequest(tabId: number): ICancelledRequest
|
||||
parseAucons(details: IDetails): Promise<string>
|
||||
}
|
||||
|
||||
export class AutoContain implements IAutoContain {
|
||||
private cancelledRequests: ICancelledRequest[] = []
|
||||
|
||||
autoContain = async (
|
||||
details: IDetails,
|
||||
): Promise<browser.webRequest.BlockingResponse> => {
|
||||
// No autocontain directives, no nothing.
|
||||
let aucons = Config.get("autocontain")
|
||||
if (Object.keys(aucons).length === 0) return { cancel: false }
|
||||
|
||||
// Do not handle private tabs or invalid tabIds.
|
||||
if (details.tabId === -1) return { cancel: false}
|
||||
let tab = await browser.tabs.get(details.tabId)
|
||||
if (tab.incognito) return { cancel: false }
|
||||
|
||||
// Only handle http requests.
|
||||
if (details.url.search("^https?://") < 0) return { cancel: false }
|
||||
|
||||
let cookieStoreId = await this.parseAucons(details)
|
||||
|
||||
// Silently return if we're already in the correct container.
|
||||
if (tab.cookieStoreId === cookieStoreId) return { cancel: false }
|
||||
|
||||
if (this.cancelEarly(tab, details)) return { cancel: true }
|
||||
|
||||
browser.tabs
|
||||
.create({
|
||||
url: details.url,
|
||||
cookieStoreId: cookieStoreId,
|
||||
active: tab.active,
|
||||
windowId: tab.windowId,
|
||||
})
|
||||
.then(_ => {
|
||||
if (details.originUrl) {
|
||||
window.history.back()
|
||||
} else {
|
||||
browser.tabs.remove(details.tabId)
|
||||
}
|
||||
})
|
||||
|
||||
return { cancel: true }
|
||||
}
|
||||
|
||||
//Handles the requests after the initial checks made in this.autoContain.
|
||||
cancelEarly = (tab: browser.tabs.Tab, details: IDetails): boolean => {
|
||||
if (!this.cancelledRequests[tab.id]) {
|
||||
this.cancelRequest(tab, details)
|
||||
} else {
|
||||
let cancel = false
|
||||
|
||||
if (this.cancelledRequests[tab.id].requestIds[details.requestId] || this.cancelledRequests[tab.id].urls[details.url]) {
|
||||
cancel = true
|
||||
}
|
||||
|
||||
this.cancelledRequests[tab.id].requestIds[details.requestId] = true
|
||||
this.cancelledRequests[tab.id].urls[details.url] = true
|
||||
|
||||
return cancel
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
cancelRequest = (tab: browser.tabs.Tab, details: IDetails): void => {
|
||||
this.cancelledRequests[tab.id] = {
|
||||
requestIds: {
|
||||
[details.requestId]: true,
|
||||
},
|
||||
urls: {
|
||||
[details.url]: true,
|
||||
},
|
||||
}
|
||||
|
||||
// The webRequest events onCompleted and onErrorOccurred are not 100% reliable.
|
||||
// Mozilla's contain facebook extension points this out and adds a guaranteed removal so we'll do the same.
|
||||
setTimeout(() => {
|
||||
this.clearCancelledRequests(tab.id)
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
getCancelledRequest = (tabId: number): ICancelledRequest => {
|
||||
return this.cancelledRequests[tabId]
|
||||
}
|
||||
|
||||
// Clear the cancelled requests.
|
||||
clearCancelledRequests = (tabId: number): void => {
|
||||
if (this.cancelledRequests[tabId]) {
|
||||
delete this.cancelledRequests[tabId]
|
||||
}
|
||||
}
|
||||
|
||||
// Parses autocontain directives and returns valid cookieStoreIds or errors.
|
||||
parseAucons = async (details): Promise<string> => {
|
||||
let aucons = Config.get("autocontain")
|
||||
const ausites = Object.keys(aucons)
|
||||
const aukeyarr = ausites.filter(
|
||||
e => details.url.search("^https?://[^/]*" + e + "/") >= 0,
|
||||
)
|
||||
if (aukeyarr.length > 1) {
|
||||
logger.error(
|
||||
"Too many autocontain directives match this url. Not containing.",
|
||||
)
|
||||
return "firefox-default"
|
||||
} else if (aukeyarr.length === 0) {
|
||||
return "firefox-default"
|
||||
} else {
|
||||
let containerExists = await Container.exists(aucons[aukeyarr[0]])
|
||||
if (!containerExists) {
|
||||
if (Config.get("auconcreatecontainer")) {
|
||||
await Container.create(aucons[aukeyarr[0]])
|
||||
} else {
|
||||
logger.error(
|
||||
"Specified container doesn't exist. consider setting 'auconcreatecontainer' to true",
|
||||
)
|
||||
}
|
||||
}
|
||||
return await Container.getId(aucons[aukeyarr[0]])
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import { browserBg } from "./webext"
|
||||
import * as Fuse from "fuse.js"
|
||||
import * as Logging from "../logging"
|
||||
const logger = new Logging.Logger("containers")
|
||||
|
||||
|
@ -176,17 +177,26 @@ export async function getAll(): Promise<any[]> {
|
|||
return await browser.contextualIdentities.query({})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name The container name
|
||||
* @returns The cookieStoreId of the first match of the query.
|
||||
/** Fetches the cookieStoreId of a given container
|
||||
|
||||
Note: all checks are case insensitive.
|
||||
|
||||
@param name The container name
|
||||
@returns The cookieStoreId of the first match of the query.
|
||||
*/
|
||||
export async function getId(name: string): Promise<string> {
|
||||
try {
|
||||
return (await browser.contextualIdentities.query({ name: name }))[0][
|
||||
"cookieStoreId"
|
||||
]
|
||||
let containers = await getAll()
|
||||
let res = containers.filter(
|
||||
c => c.name.toLowerCase() === name.toLowerCase(),
|
||||
)
|
||||
if (res.length !== 1) {
|
||||
throw new Error("")
|
||||
} else {
|
||||
return res[0]["cookieStoreId"]
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
logger.error(
|
||||
"[Container.getId] could not find a container with that name.",
|
||||
)
|
||||
}
|
||||
|
@ -197,32 +207,25 @@ export async function getId(name: string): Promise<string> {
|
|||
@param partialName The (partial) name of the container.
|
||||
*/
|
||||
export async function fuzzyMatch(partialName: string): Promise<string> {
|
||||
let containers = await getAll()
|
||||
let exactMatch = containers.filter(c => {
|
||||
return c.name.toLowerCase() === partialName.toLowerCase()
|
||||
})
|
||||
let fuseOptions = {
|
||||
id: "cookieStoreId",
|
||||
shouldSort: true,
|
||||
threshold: 0.5,
|
||||
location: 0,
|
||||
distance: 100,
|
||||
mimMatchCharLength: 3,
|
||||
keys: ["name"],
|
||||
}
|
||||
|
||||
if (exactMatch.length === 1) {
|
||||
return exactMatch[0]["cookieStoreId"]
|
||||
} else if (exactMatch.length > 1) {
|
||||
let containers = await getAll()
|
||||
let fuse = new Fuse(containers, fuseOptions)
|
||||
let res = fuse.search(partialName)
|
||||
|
||||
if (res.length >= 1) return res[0] as string
|
||||
else {
|
||||
throw new Error(
|
||||
"[Container.fuzzyMatch] more than one container with this name exists.",
|
||||
"[Container.fuzzyMatch] no container matched that string",
|
||||
)
|
||||
} else {
|
||||
let fuzzyMatches = containers.filter(c => {
|
||||
return c.name.toLowerCase().indexOf(partialName.toLowerCase()) > -1
|
||||
})
|
||||
if (fuzzyMatches.length === 1) {
|
||||
return fuzzyMatches[0]["cookieStoreId"]
|
||||
} else if (fuzzyMatches.length > 1) {
|
||||
throw new Error(
|
||||
"[Container.fuzzyMatch] ambiguous match, provide more characters",
|
||||
)
|
||||
} else {
|
||||
throw new Error(
|
||||
"[Container.fuzzyMatch] no container matched that string",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import * as Messaging from "./messaging"
|
||||
import * as Container from "./lib/containers"
|
||||
import * as UrlUtil from "./url_util"
|
||||
import * as config from "./config"
|
||||
import * as csp from "csp-serdes"
|
||||
import Logger from "./logging"
|
||||
|
|
|
@ -96,8 +96,8 @@ class ScrollingData {
|
|||
this.endPos = this.elem[this.pos] + distance
|
||||
this.duration = duration
|
||||
// If we're already scrolling we don't need to try to scroll
|
||||
if (this.scrolling) return true;
|
||||
(this.elem.style as any).scrollBehavior = "unset"
|
||||
if (this.scrolling) return true
|
||||
;(this.elem.style as any).scrollBehavior = "unset"
|
||||
this.scrolling = this.scrollStep()
|
||||
if (this.scrolling)
|
||||
// If the element can be scrolled, scroll until animation completion
|
||||
|
|
16
src/static/clippy/containers.md
Normal file
16
src/static/clippy/containers.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
# Containers
|
||||
|
||||
Containers are a Firefox feature that lets the user separate their browsing into different isolated contexts based on the user's preference.
|
||||
The perceived benefits of this feature are as described by the Firefox Test Pilot [blog](https://medium.com/firefox-test-pilot/firefox-containers-are-go-ed2e3533b6e3):
|
||||
|
||||
* **Online Privacy:** Online ads and cookies cannot follow users from one Container to the next.
|
||||
* **Account Management:** Multi-account users can stay logged in to multiple account instances at the same time.
|
||||
* **Organization:** For heavy tab users, Containers add a layer of visual organization to the Firefox interface.
|
||||
|
||||
### Container related commands
|
||||
* `containercreate name [color] [icon]` Creates a new container. Supplying `name` only will create a container called `name`, a random color and the fingerprint icon.
|
||||
* `containerupdate name newname color icon` Updates the container.
|
||||
* `containerclose name` Closes all tabs in a specified container.
|
||||
* `containerdelete name` Deletes a container, calls `containerclose` before deletion
|
||||
|
||||
The <a href='./help.html' rel='next'>final page</a> describes how you can get further help. <a href='./settings.html' rel="prev"></a>
|
|
@ -18,4 +18,4 @@ Here we will briefly summarise some of the main settings:
|
|||
* excmds
|
||||
* aliases for command mode: the things on the left actually run the commands on the right. The most interesting one of these is `current_url`, which is how the binds for O, W and T (`bind T`) work.
|
||||
|
||||
The <a href='./help.html' rel='next'>final page</a> describes how you can get further help. <a href='./command_mode.html' rel="prev"></a>
|
||||
The [next page](./containers.html) will talk about how to operate firefox containers with tridactyl. <a href='./command_mode.html' rel="prev"></a>
|
||||
|
|
Loading…
Add table
Reference in a new issue