Merge branch 'master' of github.com:cmcaine/tridactyl into hint

This commit is contained in:
Oliver Blanthorn 2018-08-01 14:03:36 +01:00
commit 74117e29ee
No known key found for this signature in database
GPG key ID: 2BB8C36BB504BFF3
14 changed files with 433 additions and 128 deletions

View file

@ -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.

View file

@ -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:

View file

@ -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

View file

@ -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"],
)
// }}}

View file

@ -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.

View file

@ -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)`

View file

@ -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

View file

@ -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
View 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]])
}
}
}

View file

@ -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",
)
}
}
}

View file

@ -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"

View file

@ -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

View 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>

View file

@ -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>