remote/origin conflict resolved

This commit is contained in:
Bruno Oliveira 2018-05-15 12:56:43 -03:00
commit 6b8922670d
24 changed files with 2481 additions and 2375 deletions

47
.tridactylrc Normal file
View file

@ -0,0 +1,47 @@
" bovine3dom's dogfood
" Move this to $XDG_CONFIG_DIR/tridactyl/tridactylrc (that's
" ~/.config/tridactyl/tridactylrc to mere mortals) or ~/.tridactylrc and
" install the native messenger (:installnative in Tridactyl). Run :source to
" get it in the browser, or just restart.
" NB: If you want "vim-like" behaviour where removing a line from
" here makes the setting disappear, uncomment the line below.
"sanitise tridactyllocal tridactylsync
"
" Binds
"
" Comment toggler for Reddit and Hacker News
bind ;c hint -c [class*="expand"],[class="togg"]
"
" Misc settings
"
set editorcmd st vim
" Sane hinting mode
set hintfiltermode vimperator-reflow
set hintchars 4327895610
" Make Tridactyl work on more sites at the expense of some security
set csp clobber
" Make quickmarks for the sane Tridactyl issue view
quickmark t https://github.com/cmcaine/tridactyl/issues?utf8=%E2%9C%93&q=sort%3Aupdated-desc+
"
" URL redirects
"
" New reddit is bad
autocmd DocStart www.reddit.com urlmodify -t www old
" Mosquito nets won't make themselves
autocmd DocStart www.amazon.co.uk urlmodify -t www smile
" This will have to do until someone writes us a nice syntax file :)
" vim: set filetype=vim:

View file

@ -1,12 +1,77 @@
# Tridactyl changelog
## Release 1.11.0 / Unreleased
## Release 1.12.0 / 2018-05-13
- Add container support
- `hint` will now open links in the current container
- there is a new setting, `set tabopencontaineraware [false|true]`, which will make `tabopen` open new tabs in the current container
- Add extra `<CA-Esc>` bind to toggle ignore mode by popular demand
- Fix errors related to missing native messenger on Firefox launch
## Release 1.11.2 / 2018-05-11
- Hotfix to prevent "config undefined" errors on browser start if no rc file was found
- It was mysteriously only reproducible sometimes...
- Make newtab changelog a bit wider
## Release 1.11.1 / 2018-05-11
- **Add "tridactylrc" support**
- Stick a bunch of commands you want to run at startup in one of:
- `$XDG_CONFIG_DIR/tridactyl/tridactylrc`
- `~/.config/tridactyl/tridactylrc`
- `~/.tridactylrc`
- [Example file available here](https://github.com/cmcaine/tridactyl/blob/master/.tridactylrc)
- You can run any file you want with `source [absolute path to file]`. Bonus points if you can think of something sensible to do with `source` in an `autocmd`.
- If you want vim-style configuration where nothing persists except that which is in the rc file, simply add `sanitise tridactyllocal tridactylsync` to the top of your rc file.
- Only whole-line comments are supported at the moment, in the VimL style where lines start with a quote mark: "
- Native messenger updated to 0.1.3
- Add rc file reader
- Add ability to read environment variables
- Make read understand ~ and environment variables (used in `source`)
- Readme updated
- Add statistics page and `guiset`
- Bug fixes
- `guiset` can now cope with multiple Firefox instances running simultaneously provided they are started with profiles explicitly via the command line.
- Deprecations
- Remove buffers,tabs as promised
- Inform people pressing `I` of the new bind
## Release 1.11.0 / 2018-05-09
- You can now edit the Firefox GUI from Tridactyl with `guiset`. You must restart Firefox after using `guiset` to see the effects.
- e.g, `guiset gui none` or `guiset gui full`.
- see all the options with `help guiset` and following the links.
- **Only minimally tested. Back up your precious userChrome.css if you care about it!**
- You can now choose to bypass [CSP](https://en.wikipedia.org/wiki/Content_Security_Policy) on all sites with `set csp clobber`. If you change your mind, just `unset csp`, and restart your browser.
- This, for example, allows Tridactyl to run on pages such as https://raw.githubusercontent.com/cmcaine/tridactyl/master/CHANGELOG.md, but it could also allow other scripts to run on pages, making the Internet as dangerous as it was about 2 or 3 years ago before CSP was introduced.
- Once this [bug](https://bugzilla.mozilla.org/show_bug.cgi?id=1267027) in Firefox is fixed, you won't have to clobber CSP.
- Tridactyl will no longer update while the browser is running in an attempt to fix issues where the add-on would be unresponsive after an update; instead, it will only update on browser launch.
- This includes manual updates via `about:addons`. You'll need to restart the browser after clicking "Check for updates".
- `set newtab news.bbc.co.uk` etc. now looks much less janky
- Minor new features
- Add !s alias for silent exclaim
- `termite` and `terminator` support with `set editorcmd auto`
- Allow binding <Esc> (not recommended...)
- AMO explains why we need each new permission
- Native messenger documentation improved, making it clear that we haven't reimplemented IRC in the browser.
- Minor bug fixes
- Remove pixel gap under command bar (#442)
- Native installer no longer requires pip and supports Debian's `which`
- Help page links are more legible on rubbish screens
- Turn 'q' and 'qall' into aliases
- Fix typo regarding binding of special keys on help page
- `focusinput` is now better at finding elements to focus
## Release 1.10.1 / 2018-05-04
- Add tabcloseallto{right,left} bound to `gx0` and `gx$`

View file

@ -2,6 +2,18 @@
set -e
echoerr() {
red="\033[31m"
normal="\e[0m"
echo -e "$red$@$normal" >&2
}
sedEscape() {
sed 's/[&/\]/\\&/g' <<< "$@"
}
trap "echoerr 'Failed to install!'" ERR
# To install, curl -fsSl 'url to this script' | bash
XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config/tridactyl}"
@ -35,21 +47,17 @@ else
curl -sS --create-dirs -o "$native_file" "$native_loc"
fi
native_file_escaped=$(sed 's/[&/\]/\\&/g' <<< "$native_file_final")
sed -i.bak "s/REPLACE_ME_WITH_SED/$native_file_escaped/" "$manifest_file"
sed -i.bak "s/REPLACE_ME_WITH_SED/$(sedEscape "$native_file_final")/" "$manifest_file"
chmod +x $native_file
# Requirements for native messenger
python_path=$(which python3)
pip_path=$(which pip3)
python_file_escaped=$(sed 's/[&/\]/\\&/g' <<< "$python_path")
if [[ -x "$python_path" ]] && [[ -x "$pip_path" ]]; then
sed -i.bak "1s/.*/#!$python_file_escaped/" "$native_file"
python_path=$(which python3) || python_path=""
if [[ -x "$python_path" ]]; then
sed -i.bak "1s/.*/#!$(sedEscape "$python_path")/" "$native_file"
mv "$native_file" "$native_file_final"
else
echo "Error: Python 3 and pip3 must exist in PATH."
echo "Please install them and run this script again."
echoerr "Error: Python 3 must exist in PATH."
echoerr "Please install it and run this script again."
exit 1
fi

View file

@ -8,8 +8,7 @@ import struct
import subprocess
import tempfile
VERSION = "0.1.1"
VERSION = "0.1.3"
class NoConnectionError(Exception):
""" Exception thrown when stdin cannot be read """
@ -22,11 +21,10 @@ def eprint(*args, **kwargs):
print(*args, file=sys.stderr, flush=True, **kwargs)
def _getenv(variable, default):
def getenv(variable, default):
""" Get an environment variable value, or use the default provided """
return os.environ.get(variable) or default
def getMessage():
"""Read a message from stdin and decode it.
@ -66,6 +64,43 @@ def sendMessage(encodedMessage):
sys.stdout.buffer.flush()
def findUserConfigFile():
""" Find a user config file, if it exists. Return the file path, or None
if not found
"""
home = os.path.expanduser('~')
config_dir = getenv('XDG_CONFIG_HOME', os.path.expanduser('~/.config'))
# Will search for files in this order
candidate_files = [
os.path.join(config_dir, "tridactyl", "tridactylrc"),
os.path.join(home, '.tridactylrc')
]
config_path = None
# find the first path in the list that exists
for path in candidate_files:
if os.path.isfile(path):
config_path = path
break
return config_path
def getUserConfig():
# look it up freshly each time - the user could have moved or killed it
cfg_file = findUserConfigFile()
# no file, return
if not cfg_file:
return None
# for now, this is a simple file read, but if the files can
# include other files, that will need more work
return open(cfg_file, 'r').read()
def handleMessage(message):
""" Generate reply from incoming message. """
cmd = message["cmd"]
@ -74,6 +109,13 @@ def handleMessage(message):
if cmd == 'version':
reply = {'version': VERSION}
elif cmd == 'getconfig':
file_content = getUserConfig()
if file_content:
reply['content'] = file_content
else:
reply['code'] = 'File not found'
elif cmd == 'run':
commands = message["command"]
@ -92,7 +134,7 @@ def handleMessage(message):
elif cmd == 'read':
try:
with open(message["file"], "r") as file:
with open(os.path.expandvars(os.path.expanduser(message["file"])), "r") as file:
reply['content'] = file.read()
reply['code'] = 0
except FileNotFoundError:
@ -116,6 +158,9 @@ def handleMessage(message):
file.write(message["content"])
reply['content'] = filepath
elif cmd == 'env':
reply['content'] = getenv(message["var"], "")
else:
reply = {'cmd': 'error', 'error': 'Unhandled message'}
eprint('Unhandled message: {}'.format(message))

4134
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -34,7 +34,7 @@
},
"scripts": {
"build": "scripts/build.sh",
"watch": "chokidar src scripts --initial --silent -i 'src/excmds_{background,content}.ts' -i 'src/static/docs' -c 'npm run build'",
"watch": "echo 'watch is broken, use build instead'; exit 0; chokidar src scripts --initial --silent -i 'src/excmds_{background,content}.ts' -i 'src/static/docs' -c 'npm run build'",
"clean": "rm -rf build generated",
"test": "npm run build && jest --silent",
"update-buildsystem": "rm -rf src/node_modules; npm run clean"

View file

@ -117,7 +117,7 @@ See `:help bind` for details about this command.
- Navigation to any about:\* pages using `:open` requires the native messenger.
- Firefox will not load Tridactyl on addons.mozilla.org, about:\*, some file:\* URIs, view-source:\*, or data:\*. On these pages Ctrl-L (or F6), Ctrl-Tab and Ctrl-W are your escape hatches.
- Tridactyl does not currently support changing/hiding the Firefox GUI, but you can do it yourself by changing your userChrome. We've developed [quite a good one](src/static/userChrome-minimal.css) that makes windowed Firefox behave more like full-screen mode, but it's well commented, so you can make your own.
- Tridactyl now supports changing the Firefox GUI if you have the native messenger installed via `guiset`. There's quite a few options available, but `guiset gui none` is probably what you want, perhaps followed up with `guiset tabs always`.
## Frequently asked questions
@ -195,6 +195,9 @@ See `:help bind` for details about this command.
Press `j` and see if you scroll down :) There's no status line yet: see [#210](https://github.com/cmcaine/tridactyl/issues/210).
- Does anyone actually use Tridactyl?
In addition to the developers, some other people do. Mozilla keeps tabs on them [here](https://addons.mozilla.org/en-US/firefox/addon/tridactyl-vim/statistics/?last=30).
## Contributing

View file

@ -27,6 +27,7 @@ import * as gobble_mode from "./parsers/gobblemode"
import * as input_mode from "./parsers/inputmode"
import * as itertools from "./itertools"
import * as keyseq from "./keyseq"
import * as request from "./requests"
import * as native from "./native_background"
import * as msgsafe from "./msgsafe"
import state from "./state"
@ -47,8 +48,37 @@ import * as webext from "./lib/webext"
keydown_background,
native,
keyseq,
request,
msgsafe,
state,
webext,
l: prom => prom.then(console.log).catch(console.error),
})
// This should be removed once https://bugzilla.mozilla.org/show_bug.cgi?id=1267027 is fixed
let cspListener
if (config.get("csp") == "clobber") {
cspListener = browser.webRequest.onHeadersReceived.addListener(
request.addurltocsp,
{ urls: ["<all_urls>"], types: ["main_frame"] },
["blocking", "responseHeaders"],
)
}
browser.storage.onChanged.addListener((changes, areaname) => {
if (config.get("csp") == "clobber") {
cspListener = browser.webRequest.onHeadersReceived.addListener(
request.addurltocsp,
{ urls: ["<all_urls>"], types: ["main_frame"] },
["blocking", "responseHeaders"],
)
} else {
// This doesn't work. :(
// browser.webRequest.onHeadersReceived.removeListener(cspListener)
}
})
// Prevent Tridactyl from being updated while it is running in the hope of fixing #290
browser.runtime.onUpdateAvailable.addListener(_ => {})
// Try to load an RC file
excmds.source_quiet()

View file

@ -123,6 +123,8 @@ const DEFAULTS = o({
";#": "hint -#",
";v": "hint -W exclaim_quiet mpv",
"<S-Insert>": "mode ignore",
"<CA-Esc>": "mode ignore",
I: "fillcmdline Ignore mode is now toggled by pressing <S-Insert>",
a: "current_url bmark",
A: "bmark",
zi: "zoom 0.1 true",
@ -160,6 +162,8 @@ const DEFAULTS = o({
tlast: "tablast",
bd: "tabclose",
bdelete: "tabclose",
quit: "tabclose",
q: "tabclose",
sanitize: "sanitise",
tutorial: "tutor",
h: "help",
@ -252,10 +256,18 @@ const DEFAULTS = o({
"curl -fsSl https://raw.githubusercontent.com/cmcaine/tridactyl/master/native/install.sh | bash",
profiledir: "auto",
// Container settings
// If enabled, tabopen opens a new tab in the currently active tab's container.
tabopencontaineraware: "false",
// Performance related settings
// number of most recent results to ask Firefox for. We display the top 20 or so most frequently visited ones.
historyresults: "50",
// Security settings
csp: "untouched", // change this to "clobber" to ruin the CSP of all sites and make Tridactyl run a bit better on some of them, e.g. raw.github*
})
/** Given an object and a target, extract the target if it exists, else return undefined

29
src/config_rc.ts Normal file
View file

@ -0,0 +1,29 @@
import * as Controller from "./controller"
import * as Native from "./native_background"
import Logger from "./logging"
const logger = new Logger("rc")
export async function source(filename = "auto") {
let rctext = ""
if (filename == "auto") {
rctext = await Native.getrc()
} else {
rctext = (await Native.read(filename)).content
}
if (rctext === undefined) return false
runRc(rctext)
return true
}
export async function runRc(rc: string) {
for (let cmd of rcFileToExCmds(rc)) {
await Controller.acceptExCmd(cmd)
}
}
export function rcFileToExCmds(rcText: string): string[] {
const excmds = rcText.split("\n")
// Remove comment lines
return excmds.filter(x => x != "" && !x.trim().startsWith('"'))
}

View file

@ -83,7 +83,7 @@ function* ParserController() {
acceptExCmd(exstr)
} catch (e) {
// Rumsfeldian errors are caught here
logger.error("Tridactyl ParserController fatally wounded:", e)
logger.error("An error occurred in the controller: ", e)
}
}
}

View file

@ -6,21 +6,27 @@ import * as CSS from "css"
// type: "rule", selectors: [string], declarations: [
// {type: "declaration", property: string, value: string}
/** Find rules in sheet that match selector */
export function findCssRules(
ruleToFind: string,
selector: string,
sheet: CSS.Stylesheet,
): number[] {
const filtSheet = [...sheet.stylesheet.rules.entries()].filter(x => {
const rule = x[1]
return (
rule.type == "rule" &&
rule["selectors"].filter(sel => sel == ruleToFind).length > 0
)
return rule.type == "rule" && rule["selectors"].indexOf(selector) > -1
})
return filtSheet.map(x => x[0])
}
// TODO: make this more flexible with cleverer matching of selectors, merging of options
/** Rulename -> { name: <selector>, options: { <option-name>: <css-string> } }
*
* Multi-level map of rulename, options available for rule and css for each option.
*
* *findCssRules and changeSingleCss rely on the selector not containing a comma.*
*
* TODO: make this more flexible with cleverer matching of selectors, merging of options
*
*/
export const potentialRules = {
hoverlink: {
name: `statuspanel[type="overLink"]`,
@ -28,8 +34,8 @@ export const potentialRules = {
none: `display: none !important;`,
right: `right: 0; display: inline;`,
left: ``,
"top-left": `top: 2em; display: inline;`,
"top-right": `top: 2em; right: 0; display: inline;`,
"top-left": `top: 2em; z-index: 2; display: inline;`,
"top-right": `top: 2em; z-index: 2; right: 0; display: inline;`,
},
},
tabstoolbar: {
@ -107,6 +113,10 @@ export const potentialRules = {
//
// was just a simple show/hide if the characters appeared in the setting
/** Rules that index into potentialRules or metaRules
*
* Please don't add cycles to this table :)
*/
export const metaRules = {
gui: {
none: {
@ -127,14 +137,17 @@ export const metaRules = {
tabs: {
none: {
tabstoolbar: "none",
navtoolboxunfocused: "hide",
},
always: {
tabstoolbar: "show",
tabstoolbarunfocused: "show",
navtoolboxunfocused: "show",
},
autohide: {
tabstoolbar: "show",
tabstoolbarunfocused: "hide",
navtoolboxunfocused: "hide",
},
},
navbar: {
@ -151,39 +164,40 @@ export const metaRules = {
},
}
/** Add desired non-meta rule to stylesheet replacing existing rule with the same selector if present */
export function changeSingleCss(
rulename: string,
optionname: string,
sheet: CSS.Stylesheet,
): CSS.Stylesheet {
const ruleInds = findCssRules(potentialRules[rulename]["name"], sheet)
const desRule =
potentialRules[rulename]["name"] +
" {" +
potentialRules[rulename]["options"][optionname] +
"}"
const miniSheet = CSS.parse(desRule).stylesheet.rules[0]
if (ruleInds.length > 0) {
sheet.stylesheet.rules[ruleInds[0]] = miniSheet
const selector = potentialRules[rulename]["name"]
const newRule = `${selector} {
${potentialRules[rulename]["options"][optionname]}
}`
const miniSheet = CSS.parse(newRule).stylesheet.rules[0]
// Find pre-existing rules
const oldRuleIndexes = findCssRules(selector, sheet)
if (oldRuleIndexes.length > 0) {
sheet.stylesheet.rules[oldRuleIndexes[0]] = miniSheet
} else {
sheet.stylesheet.rules = sheet.stylesheet.rules.concat(miniSheet)
}
return sheet
}
/** Apply rule to stylesheet. rulename, optionname identify a rule. They may be meta rules */
export function changeCss(
rulename: string,
optionname: string,
sheet: CSS.Stylesheet,
): CSS.Stylesheet {
if (rulename in metaRules) {
for (let rule of Object.keys(metaRules[rulename][optionname])) {
const metarule = metaRules[rulename][optionname]
for (let rule of Object.keys(metarule)) {
// have a metarule call itself for hours of fun
sheet = changeCss(
rule,
metaRules[rulename][optionname][rule],
sheet,
)
sheet = changeCss(rule, metarule[rule], sheet)
}
} else sheet = changeSingleCss(rulename, optionname, sheet)
return sheet

View file

@ -87,7 +87,7 @@
// Shared
import * as Messaging from "./messaging"
import { l, browserBg, activeTabId } from "./lib/webext"
import { l, browserBg, activeTabId, activeTabContainerId } from "./lib/webext"
import state from "./state"
import * as UrlUtil from "./url_util"
import * as config from "./config"
@ -97,7 +97,6 @@ import * as Logging from "./logging"
const logger = new Logging.Logger("excmds")
import Mark from "mark.js"
import * as CSS from "css"
import * as semverCompare from "semver-compare"
//#content_helper
// {
@ -118,6 +117,7 @@ import { ModeName } from "./state"
import * as keydown from "./keydown_background"
import { activeTab, firefoxVersionAtLeast, openInNewTab } from "./lib/webext"
import * as CommandLineBackground from "./commandline_background"
import * as rc from "./config_rc"
//#background_helper
import * as Native from "./native_background"
@ -167,7 +167,7 @@ export async function getinput() {
*/
//#background
export async function editor() {
if (!await nativegate()) return
if (!await Native.nativegate()) return
const file = (await Native.temp(await getinput())).content
fillinput((await Native.editor(file)).content)
// TODO: add annoying "This message was written with [Tridactyl](https://addons.mozilla.org/en-US/firefox/addon/tridactyl-vim/)"
@ -217,17 +217,17 @@ export async function guiset(rule: string, option: string) {
// Could potentially fall back to sending minimal example to clipboard if native not installed
// Check for native messenger and make sure we have a plausible profile directory
if (!await nativegate("0.1.1")) return
if (!await Native.nativegate("0.1.1")) return
let profile_dir = ""
if (config.get("profiledir") === "auto") {
if (["linux", "openbsd", "mac"].includes((await browser.runtime.getPlatformInfo()).os)) profile_dir = await Native.getProfileDir()
else {
fillcmdline("Please set your profile directory (found on about:support) via `set profiledir [profile directory]`")
return
}
} else profile_dir = config.get("profiledir")
if (config.get("profiledir") === "auto" && ["linux", "openbsd", "mac"].includes((await browser.runtime.getPlatformInfo()).os)) {
try {
profile_dir = await Native.getProfileDir()
} catch (e) {}
} else {
profile_dir = config.get("profiledir")
}
if (profile_dir == "") {
logger.error("Profile not found.")
fillcmdline("Please set your profile directory (found on about:support) via `set profiledir [profile directory]`")
return
}
@ -241,7 +241,8 @@ export async function guiset(rule: string, option: string) {
// Modify and write new CSS
if (cssstr === "") cssstr = `@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");`
let stylesheet = CSS.parse(cssstr)
let stylesheetDone = CSS.stringify(css_util.changeCss(rule, option, stylesheet))
// Trim due to https://github.com/reworkcss/css/issues/114
let stylesheetDone = CSS.stringify(css_util.changeCss(rule, option, stylesheet)).trim()
Native.write(profile_dir + "/chrome/userChrome.css", stylesheetDone)
}
@ -263,37 +264,11 @@ export function cssparse(...css: string[]) {
//#background
export async function nativeopen(url: string, ...firefoxArgs: string[]) {
if (firefoxArgs.length === 0) firefoxArgs = ["--new-tab"]
if (await nativegate()) {
if (await Native.nativegate()) {
Native.run(config.get("browser") + " " + firefoxArgs.join(" ") + " " + url)
}
}
/**
* Used internally to gate off functions that use the native messenger. Gives a helpful error message in the command line if the native messenger is not installed, or is the wrong version.
*/
//#background
export async function nativegate(version = "0", interactive = true): Promise<Boolean> {
if (["win", "android"].includes((await browser.runtime.getPlatformInfo()).os)) {
if (interactive == true) fillcmdline("# Tridactyl's native messenger doesn't support your operating system, yet.")
return false
}
try {
const actualVersion = await Native.getNativeMessengerVersion()
if (actualVersion !== undefined) {
if (semverCompare(version, actualVersion) > 0) {
if (interactive == true) fillcmdline("# Please update to native messenger " + version + ", for example by running `:updatenative`.")
// TODO: add update procedure and document here.
return false
}
return true
} else if (interactive == true) fillcmdline("# Native messenger not found. Please run `:installnative` and follow the instructions.")
return false
} catch (e) {
if (interactive == true) fillcmdline("# Native messenger not found. Please run `:installnative` and follow the instructions.")
return false
}
}
/**
* Run command in /bin/sh (unless you're on Windows), and print the output in the command line. Non-zero exit codes and stderr are ignored, currently.
*
@ -337,6 +312,34 @@ export async function installnative() {
fillcmdline("# Installation command copied to clipboard. Please paste and run it in your shell to install the native messenger.")
}
/**
* 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.
*
* 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://www.github.com/cmcaine/tridactyl/master/.tridactylrc) if you want it.
*
* @param fileArr the file to open. Must be an absolute path, but can contain environment variables and things like ~.
*/
//#background
export async function source(...fileArr: string[]) {
const file = fileArr.join(" ") || undefined
if (await Native.nativegate("0.1.3")) if (!await rc.source(file)) logger.error("Could not find RC file")
}
/**
* Same as [[source]] but suppresses all errors
*/
//#background
export async function source_quiet(...fileArr: string[]) {
try {
const file = fileArr.join(" ") || undefined
if (await Native.nativegate("0.1.3", false)) rc.source(file)
} catch (e) {
logger.info("Automatic loading of RC file failed.")
}
}
/**
* Updates the native messenger if it is installed, using our GitHub repo. This is run every time Tridactyl is updated.
*
@ -344,7 +347,7 @@ export async function installnative() {
*/
//#background
export async function updatenative(interactive = true) {
if (await nativegate("0", interactive)) {
if (await Native.nativegate("0", interactive)) {
if ((await browser.runtime.getPlatformInfo()).os === "mac") {
if (interactive) logger.error("Updating the native messenger on OSX is broken. Please use `:installnative` instead.")
return
@ -1244,7 +1247,10 @@ export async function tabopen(...addressarr: string[]) {
} else if (address != "") url = forceURI(address)
else url = forceURI(config.get("newtab"))
openInNewTab(url, { active })
activeTabContainerId().then(containerId => {
if (containerId && config.get("tabopencontaineraware") === "true") openInNewTab(url, { active: active, cookieStoreId: containerId })
else openInNewTab(url, { active })
})
}
/** Resolve a tab index to the tab id of the corresponding tab in this window.
@ -1405,18 +1411,6 @@ export async function undo() {
}
}
/** Synonym for [[tabclose]]. */
//#background
export async function quit() {
tabclose()
}
/** Convenience shortcut for [[quit]]. */
//#background
export async function q() {
tabclose()
}
/** Move the current tab to be just in front of the index specified.
Known bug: This supports relative movement, but autocomple doesn't know
@ -1697,26 +1691,6 @@ export async function clipboard(excmd: "open" | "yank" | "yankshort" | "yankcano
CommandLineBackground.hide()
}
// {{{ Buffer/completion stuff
/** Equivalent to `fillcmdline buffer`
Sort of Vimperator alias
*/
//#background
export async function tabs() {
fillcmdline("Deprecated. If anyone actually uses this, they should file an issue on GitHub.")
}
/** Equivalent to `fillcmdline buffer`
Sort of Vimperator alias
*/
//#background
export async function buffers() {
tabs()
}
/** Change active tab.
@param index

View file

@ -537,7 +537,7 @@ const HINTTAGS_saveable = `
[href]:not([href='#'])
`
import { openInNewTab } from "./lib/webext"
import { openInNewTab, activeTabContainerId } from "./lib/webext"
/** if `target === _blank` clicking the link is treated as opening a popup and is blocked. Use webext API to avoid that. */
function simulateClick(target: HTMLElement) {
@ -550,7 +550,18 @@ function simulateClick(target: HTMLElement) {
(target as HTMLAnchorElement).target === "_blank" ||
(target as HTMLAnchorElement).target === "_new"
) {
openInNewTab((target as HTMLAnchorElement).href, { related: true })
// Try to open the new tab in the same container as the current one.
activeTabContainerId().then(containerId => {
if (containerId)
openInNewTab((target as HTMLAnchorElement).href, {
related: true,
cookieStoreId: containerId,
})
else
openInNewTab((target as HTMLAnchorElement).href, {
related: true,
})
})
} else {
DOM.mouseEvent(target, "click")
// DOM.focus has additional logic for focusing inputs
@ -563,10 +574,21 @@ function hintPageOpenInBackground(selectors = HINTTAGS_selectors) {
hint.target.focus()
if (hint.target.href) {
// Try to open with the webext API. If that fails, simulate a click on this page anyway.
openInNewTab(hint.target.href, {
active: false,
related: true,
}).catch(() => simulateClick(hint.target))
// Try to open the new tab in the same container as the current one.
activeTabContainerId().then(containerId => {
if (containerId) {
openInNewTab(hint.target.href, {
active: false,
related: true,
cookieStoreId: containerId,
}).catch(() => simulateClick(hint.target))
} else {
openInNewTab(hint.target.href, {
active: false,
related: true,
}).catch(() => simulateClick(hint.target))
}
})
} else {
// This is to mirror vimperator behaviour.
simulateClick(hint.target)

View file

@ -67,6 +67,11 @@ export async function activeTabId() {
return (await activeTab()).id
}
//#background_helper
export async function activeTabContainerId() {
return (await activeTab()).cookieStoreId
}
/** Compare major firefox versions */
export async function firefoxVersionAtLeast(desiredmajor: number) {
const versionstr = (await browserBg.runtime.getBrowserInfo()).version
@ -88,12 +93,17 @@ export async function firefoxVersionAtLeast(desiredmajor: number) {
*/
export async function openInNewTab(
url: string,
kwargs: { active?; related? } = { active: true, related: false },
kwargs: { active?; related?; cookieStoreId? } = {
active: true,
related: false,
cookieStoreId: undefined,
},
) {
const thisTab = await activeTab()
const options: any = {
active: kwargs.active,
url,
cookieStoreId: kwargs.cookieStoreId,
}
// Be nice to behrmann, #342

View file

@ -1,7 +1,7 @@
{
"manifest_version": 2,
"name": "Tridactyl",
"version": "1.10.1",
"version": "1.12.0",
"icons": {
"64": "static/logo/Tridactyl_64px.png",
"100": "static/logo/Tridactyl_100px.png",
@ -43,6 +43,8 @@
"bookmarks",
"browsingData",
"contextMenus",
"contextualIdentities",
"cookies",
"clipboardWrite",
"clipboardRead",
"downloads",

View file

@ -2,13 +2,23 @@
* Background functions for the native messenger interface
*/
import * as semverCompare from "semver-compare"
import * as config from "./config"
import Logger from "./logging"
const logger = new Logger("native")
const NATIVE_NAME = "tridactyl"
type MessageCommand = "version" | "run" | "read" | "write" | "temp" | "mkdir"
type MessageCommand =
| "version"
| "run"
| "read"
| "write"
| "temp"
| "mkdir"
| "eval"
| "getconfig"
| "env"
interface MessageResp {
cmd: string
version: number | null
@ -35,12 +45,23 @@ async function sendNativeMsg(
return resp as MessageResp
} catch (e) {
if (!quiet) {
logger.error(`Error sending native message:`, e)
throw e
}
}
}
export async function getrc(): Promise<string> {
const res = await sendNativeMsg("getconfig", {})
if (res.content && !res.error) {
logger.info(`Successfully retrieved fs config:\n${res.content}`)
return res.content
} else {
// Have to make this a warning as async exceptions apparently don't get caught
logger.info(`Error in retrieving config: ${res.error}`)
}
}
export async function getNativeMessengerVersion(
quiet = false,
): Promise<number> {
@ -80,10 +101,12 @@ export async function getBestEditor(): Promise<string> {
"xterm -class tridactyl_editor -e",
"uxterm -class tridactyl_editor -e",
"urxvt -e",
// "terminator -e", // NB: requires command to be in quotes, which breaks the others
// so terminator is not supported.
"alacritty -e", // alacritty is nice but takes ages to start and doesn't support class
"cool-retro-term -e",
// Terminator and termite require -e commands to be in quotes, hence the extra quote at the end.
// The closing quote is implemented in the editor function.
'terminator -e "',
'termite --class tridactyl_editor -e "',
"dbus-launch gnome-terminal --",
// I wanted to put hyper.js here as a joke but you can't start it running a command,
// which is a far better joke: a terminal emulator that you can't send commands to.
@ -120,6 +143,59 @@ export async function getBestEditor(): Promise<string> {
return cmd
}
/**
* Used internally to gate off functions that use the native messenger. Gives a
* helpful error message in the command line if the native messenger is not
* installed, or is the wrong version.
*
* @arg version: A string representing the minimal required version.
* @arg interactive: True if a message should be displayed on version mismatch.
* @return false if the required version is higher than the currently available
* native messenger version.
*/
export async function nativegate(
version = "0",
interactive = true,
): Promise<Boolean> {
if (
["win", "android"].includes(
(await browser.runtime.getPlatformInfo()).os,
)
) {
if (interactive == true)
logger.error(
"# Tridactyl's native messenger doesn't support your operating system, yet.",
)
return false
}
try {
const actualVersion = await getNativeMessengerVersion()
if (actualVersion !== undefined) {
if (semverCompare(version, actualVersion) > 0) {
if (interactive == true)
logger.error(
"# Please update to native messenger " +
version +
", for example by running `:updatenative`.",
)
// TODO: add update procedure and document here.
return false
}
return true
} else if (interactive == true)
logger.error(
"# Native messenger not found. Please run `:installnative` and follow the instructions.",
)
return false
} catch (e) {
if (interactive == true)
logger.error(
"# Native messenger not found. Please run `:installnative` and follow the instructions.",
)
return false
}
}
export async function inpath(cmd) {
return (await run("which " + cmd.split(" ")[0])).code === 0
}
@ -142,7 +218,11 @@ export async function editor(file: string, content?: string) {
config.get("editorcmd") == "auto"
? await getBestEditor()
: config.get("editorcmd")
await run(editorcmd + " " + file)
// Dirty hacks for termite and terminator support part 2.
const e = editorcmd.split(" ")[0]
if (e === "termite" || e === "terminator") {
await run(editorcmd + " " + file + '"')
} else await run(editorcmd + " " + file)
return await read(file)
}
@ -161,24 +241,84 @@ export async function mkdir(dir: string, exist_ok: boolean) {
export async function temp(content: string) {
return sendNativeMsg("temp", { content })
}
export async function run(command: string) {
let msg = await sendNativeMsg("run", { command })
logger.info(msg)
return msg
}
/** Evaluates a string in the native messenger. This has to be python code. If
* you want to run shell strings, use run() instead.
*/
export async function pyeval(command: string): Promise<MessageResp> {
return sendNativeMsg("eval", { command })
}
export async function getenv(variable: string) {
let v = await getNativeMessengerVersion()
if (!await nativegate("0.1.2", false)) {
throw `Error: getenv needs native messenger v>=0.1.2. Current: ${v}`
}
return (await sendNativeMsg("env", { var: variable })).content
}
/** This returns the commandline that was used to start firefox.
You'll get both firefox binary (not necessarily an absolute path) and flags */
export async function ffargs(): Promise<string[]> {
// Using ' and + rather that ` because we don't want newlines
let output = await pyeval(
'handleMessage({"cmd": "run", ' +
'"command": "ps -p " + str(os.getppid()) + " -oargs="})["content"]',
)
return output.content.trim().split(" ")
}
export async function getProfileDir() {
// First, see if we can get the profile from the arguments that were given
// to Firefox
let args = await ffargs()
// --profile <path>: Start with profile at <path>
let prof = args.indexOf("--profile")
if (prof >= 0) return args[prof + 1]
// -P <profile>: Start with <profile>
// -P : Start with profile manager
// Apparently, this argument is case-insensitive
let profileName = "*"
prof = args.indexOf("-P")
if (prof < 0) prof = args.indexOf("-p")
// args.length -1 because we need to make sure -P was given a value
if (prof >= 0 && prof < args.length - 1) profileName = args[prof + 1]
// Find active profile directory automatically by seeing where the lock exists
let hacky_profile_finder = "find ../../../.mozilla/firefox -name lock"
let home = "../../.."
try {
// We try not to use a relative path because ~/.local (the directory where
// the native messenger currently sits) might actually be a symlink
home = await getenv("HOME")
} catch (e) {}
let hacky_profile_finder = `find "${home}/.mozilla/firefox" -maxdepth 2 -path '*.${profileName}/lock'`
if ((await browser.runtime.getPlatformInfo()).os === "mac")
hacky_profile_finder =
"find ../../../Library/'Application Support'/Firefox/Profiles -maxdepth 2 -name .parentlock"
let profilecmd = await run(hacky_profile_finder)
if (profilecmd.code != 0) {
return ""
} else
return profilecmd.content
.split("/")
.slice(0, -1)
.join("/")
throw new Error("Profile not found")
} else {
// Remove trailing newline
profilecmd.content = profilecmd.content.trim()
if (profilecmd.content.split("\n").length > 1) {
throw new Error(
"Multiple profiles in use. Can't tell which one you want. `set profiledir`, close other Firefox profiles or remove zombie lock files.",
)
} else {
// Get parent directory of lock file
return profilecmd.content
.split("/")
.slice(0, -1)
.join("/")
}
}
}

View file

@ -3,7 +3,10 @@ import { hasModifiers } from "../keyseq"
// Placeholder - should be moved into generic parser
export function parser(keys) {
const response = { keys: [], exstr: undefined }
if (keys[0].shiftKey && keys[0].key === "Insert") {
if (
(keys[0].shiftKey && keys[0].key === "Insert") ||
(keys[0].altKey && keys[0].ctrlKey && keys[0].key === "Escape")
) {
return { keys: [], exstr: "mode normal" }
}
return { keys: [], exstr: undefined }

32
src/requests.ts Normal file
View file

@ -0,0 +1,32 @@
import * as config from "./config"
export function addurltocsp(response) {
let headers = response["responseHeaders"]
let cspind = headers.findIndex(
header => header.name == "Content-Security-Policy",
)
// if it's found
if (cspind > -1) {
// Split the csp header up so we can manage it individually.
let csparr = [headers[cspind]["value"].split("; ")][0]
for (let i = 0; i < csparr.length; i++) {
// Add 'unsafe-inline' as a directive since we use it
if (csparr[i].indexOf("style-src") > -1) {
if (csparr[i].indexOf("'self'") > -1) {
csparr[i] = csparr[i].replace(
"'self'",
"'self' 'unsafe-inline'",
)
}
}
// Remove the element if it's a sandbox directive
if (csparr[i] === "sandbox") {
csparr.splice(i, 1)
}
}
// Join the header up after clobberin'
headers[cspind]["value"] = csparr.join("; ")
}
return { responseHeaders: headers }
}

View file

@ -29,7 +29,6 @@ input[id^="spoiler"]:checked + label {
background: #ccc;
}
input[id^="spoiler"] ~ .spoiler {
width: 90%;
height: 0;
overflow: hidden;
opacity: 0;

View file

@ -12,8 +12,6 @@ Tridactyl has to override your new tab page due to WebExtension limitations. You
- If you're enjoying Tridactyl (or not), please leave a review on [addons.mozilla.org][amo].
- **Breaking change to default settings:** ignore mode is now bound to `<S-Insert>` for both entering and leaving the mode. Previous binds are unbound.
- **NB:** Tridactyl can now run external programs on Linux and OSX if you decide to install an additional executable. Just run `:installnative` to get going, and then Ctrl-i `<C-i>` in a text box to open your editor.
@ -44,7 +42,7 @@ REPLACE_ME_WITH_THE_CHANGE_LOG_USING_SED
- You can only navigate to most about:*\file:* pages if you have Tridactyl's native executable installed.
- Firefox will not load Tridactyl on addons.mozilla.org, about:\*, some file:\* URIs, view-source:\*, or data:\*. On these pages Ctrl-L (or F6), Ctrl-Tab and Ctrl-W are your escape hatches.
- Tridactyl does not currently support changing/hiding the Firefox GUI, but you can do it yourself by changing your userChrome. There is an example file available on our repository [[2]].
- You can change the Firefox GUI with `guiset` (e.g. `guiset gui none`) if you have the native messenger installed, or you can do it yourself by changing your userChrome. There is an example file available on our repository [[2]].
- Tridactyl cannot capture key presses until web pages are loaded. You can use `:reloadall` to reload all tabs to make life more bearable, or flip `browser.sessionstore.restore_tabs_lazily` to false in `about:config`.
## Why do I see this here?

View file

@ -1,6 +1,5 @@
<html lang="en" style="display: none;">
<head>
<script src="newtab.js"></script>
<meta charset="utf-8">
<link rel="stylesheet" href="newtab.css">
<link rel="stylesheet" href="content.css">

View file

@ -889,7 +889,7 @@ body {
}
a {
color: green;
color: #05a805;
}
code {

4
src/tridactyl.d.ts vendored
View file

@ -50,3 +50,7 @@ declare function html(
strings: TemplateStringsArray,
...values: any[]
): HTMLElement
declare namespace browser.webRequest {
function filterResponseData(requestId: string): any
}