Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parsers and state in content #962

Merged
merged 11 commits into from Sep 4, 2018
Next

Janky proof of concept for per-tab state, incl mode and parsers

I've been programming on pure instinct for like five hours and I'm not
sure what exactly I did any more. I *think* that what I did was
something like:
* Split `state.ts` into `state.ts` and `content_state.ts`, then
  removed mode from state and moved it to content_state.
* Made the types in content_state.ts a hair more powerful
* Fixed all errors resulting from those two changes, in the process
  swapping state out for content_state wherever appropriate.
* Moved a couple excmds from background into content since they
  depended heavily on the mode and should live with the mode
* Split the controller in half, moving the parser logic to the content
  script and leaving acceptExCmds in the background
  version. content_controller forwards excmds to background script
  using messaging.
* Nuked the keydown_* code, since we no longer need to forward keys to
  the background.
* Went around fixing everything resulting from *those* changes, mostly
  the various perversions of keydown listeners
* Various tweaks here and there that probably didn't do anything but I
  was just changing things at random a third of the time and I really
  need to go through this and clean it up.

Things that work:
* excmds are nice now. each tab has its own commandline; they stay
  open when you navigate between tabs and you can come back to them
  after doing stuff in another tab and finish your command line input.
* keybinds that don't don't involve mode switching: `[[`, `]]`, `j`,
  `k`, `r`, etc, all still functional.
* You can turn on hinting mode and, again, navigate to another tab
  which will *not* be in hinting mode, do whatever you need to do
  there including excmds or more hinting, and then come back to finish
  the hint you started in the first tab
* You can exit hint mode by pressing escape. :P

Things that I broke:
* ...Actually quite a bunch of stuff, I think.
* I don't seem to be able to *finish* a hint any more. Sometimes I
  can't even get one key in.
  • Loading branch information...
saulrh committed Sep 1, 2018
commit d1e6a8653913a15b132f11c7731ca3a1c1bd41d6
@@ -1,29 +1,17 @@
/** Background script entry point. */

import * as Controller from "./controller"
import * as keydown_background from "./keydown_background"
import * as CommandLine from "./commandline_background"
import * as BackgroundController from "./controller_background"
import "./lib/browser_proxy_background"

// Send keys to controller
keydown_background.onKeydown.addListener(Controller.acceptKey)
// To eventually be replaced by:
// browser.keyboard.onKeydown.addListener

// Send commandline to controller
CommandLine.onLine.addListener(Controller.acceptExCmd)

// Add various useful modules to the window for debugging
import * as messaging from "./messaging"
import * as excmds from "./.excmds_background.generated"
import * as commandline_background from "./commandline_background"
import * as controller from "./controller"
import * as convert from "./convert"
import * as config from "./config"
import * as dom from "./dom"
import * as hinting_background from "./hinting_background"
import * as download_background from "./download_background"
import * as gobble_mode from "./parsers/gobblemode"
import * as itertools from "./itertools"
import * as keyseq from "./keyseq"
import * as request from "./requests"
@@ -36,15 +24,12 @@ import { AutoContain } from "./lib/autocontainers"
messaging,
excmds,
commandline_background,
controller,
convert,
config,
dom,
hinting_background,
download_background,
gobble_mode,
itertools,
keydown_background,
native,
keyseq,
request,
@@ -54,6 +39,11 @@ import { AutoContain } from "./lib/autocontainers"
l: prom => prom.then(console.log).catch(console.error),
})

// Send commandline to controller
commandline_background.onLine.addListener(BackgroundController.acceptExCmd)



// {{{ Clobber CSP

// This should be removed once https://bugzilla.mozilla.org/show_bug.cgi?id=1267027 is fixed
@@ -91,12 +81,12 @@ browser.runtime.onStartup.addListener(_ => {
let hosts = Object.keys(aucmds)
// If there's only one rule and it's "all", no need to check the hostname
if (hosts.length == 1 && hosts[0] == ".*") {
Controller.acceptExCmd(aucmds[hosts[0]])
BackgroundController.acceptExCmd(aucmds[hosts[0]])
} else {
native.run("hostname").then(hostname => {
for (let host of hosts) {
if (hostname.content.match(host)) {
Controller.acceptExCmd(aucmds[host])
BackgroundController.acceptExCmd(aucmds[host])
}
}
})
@@ -1,4 +1,4 @@
import * as Controller from "./controller"
import * as Controller from "./controller_background"
import * as Native from "./native_background"
import Logger from "./logging"
const logger = new Logger("rc")
@@ -4,14 +4,26 @@
// assigned to a name. If you want an import just for its side effects, make
// sure you import it like this:
import "./lib/html-tagged-template"
/* import "./keydown_content" */
/* import "./commandline_content" */
/* import "./excmds_content" */
/* import "./hinting" */
import * as Logging from "./logging"
const logger = new Logging.Logger("content")
logger.debug("Tridactyl content script loaded, boss!")

// Our local state
import {contentState, addContentStateChangedListener } from "./content_state"

// Hook the keyboard up to the controller
import * as ContentController from "./controller_content"
try {
document.body.addEventListener("keydown", ContentController.acceptKey)
} catch (e) {
window.addEventListener("DOMContentLoaded", () => {
document.body.addEventListener("keydown", ContentController.acceptKey)
})
}

// Add various useful modules to the window for debugging
import * as commandline_content from "./commandline_content"
import * as convert from "./convert"
@@ -21,7 +33,6 @@ import * as excmds from "./.excmds_content.generated"
import * as hinting_content from "./hinting"
import * as finding_content from "./finding"
import * as itertools from "./itertools"
import * as keydown_content from "./keydown_content"
import * as messaging from "./messaging"
import * as msgsafe from "./msgsafe"
import state from "./state"
@@ -30,28 +41,29 @@ import Mark from "mark.js"
import * as keyseq from "./keyseq"
import * as native from "./native_background"
import * as styling from "./styling"
;(window as any).tri = Object.assign(Object.create(null), {
browserBg: webext.browserBg,
commandline_content,
convert,
config,
dom,
excmds,
hinting_content,
finding_content,
itertools,
keydown_content,
logger,
Mark,
keyseq,
messaging,
msgsafe,
state,
webext,
l: prom => prom.then(console.log).catch(console.error),
native,
styling,
})
; (window as any).tri = Object.assign(Object.create(null), {
browserBg: webext.browserBg,
commandline_content,
convert,
config,
dom,
excmds,
hinting_content,
finding_content,
itertools,
logger,
Mark,
keyseq,
messaging,
msgsafe,
state,
webext,
l: prom => prom.then(console.log).catch(console.error),
native,
styling,
})

logger.info("Loaded commandline content?", commandline_content)

// Don't hijack on the newtab page.
if (webext.inContentScript()) {
@@ -141,46 +153,50 @@ config.getAsync("modeindicator").then(mode => {
}
window.addEventListener("mousemove", onMouseOut)
})

try {
// On quick loading pages, the document is already loaded
statusIndicator.textContent = state.mode || "normal"
statusIndicator.textContent = contentState.mode || "normal"
document.body.appendChild(statusIndicator)
document.head.appendChild(style)
} catch (e) {
// But on slower pages we wait for the document to load
window.addEventListener("DOMContentLoaded", () => {
statusIndicator.textContent = state.mode || "normal"
statusIndicator.textContent = contentState.mode || "normal"
document.body.appendChild(statusIndicator)
document.head.appendChild(style)
})
}

browser.storage.onChanged.addListener((changes, areaname) => {
if (areaname === "local" && "state" in changes) {
let mode = changes.state.newValue.mode
const privateMode = browser.extension.inIncognitoContext
? "TridactylPrivate"
: ""
statusIndicator.className =
"cleanslate TridactylStatusIndicator " + privateMode
if (
dom.isTextEditable(document.activeElement) &&
addContentStateChangedListener((property, oldValue, newValue) => {
if (property != "mode") {
return
}

let mode = newValue
const privateMode = browser.extension.inIncognitoContext
? "TridactylPrivate"
: ""
statusIndicator.className =
"cleanslate TridactylStatusIndicator " + privateMode
if (
dom.isTextEditable(document.activeElement) &&
!["input", "ignore"].includes(mode)
) {
statusIndicator.textContent = "insert"
// this doesn't work; statusIndicator.style is full of empty string
// statusIndicator.style.borderColor = "green !important"
// need to fix loss of focus by click: doesn't do anything here.
} else if (
mode === "insert" &&
) {
statusIndicator.textContent = "insert"
// this doesn't work; statusIndicator.style is full of empty string
// statusIndicator.style.borderColor = "green !important"
// need to fix loss of focus by click: doesn't do anything here.
} else if (
mode === "insert" &&
!dom.isTextEditable(document.activeElement)
) {
statusIndicator.textContent = "normal"
// statusIndicator.style.borderColor = "lightgray !important"
} else {
statusIndicator.textContent = mode
}
) {
statusIndicator.textContent = "normal"
// statusIndicator.style.borderColor = "lightgray !important"
} else {
statusIndicator.textContent = mode
}

if (config.get("modeindicator") !== "true") statusIndicator.remove()
})
})
@@ -192,7 +208,7 @@ config.getAsync("leavegithubalone").then(v => {
// On quick loading pages, the document is already loaded
// if (document.location.host == "github.com") {
document.body.addEventListener("keydown", function(e) {
if ("/".indexOf(e.key) != -1 && state.mode == "normal") {
if ("/".indexOf(e.key) != -1 && contentState.mode == "normal") {
e.cancelBubble = true
e.stopImmediatePropagation()
}
@@ -203,7 +219,7 @@ config.getAsync("leavegithubalone").then(v => {
window.addEventListener("DOMContentLoaded", () => {
// if (document.location.host == "github.com") {
document.body.addEventListener("keydown", function(e) {
if ("/".indexOf(e.key) != -1 && state.mode == "normal") {
if ("/".indexOf(e.key) != -1 && contentState.mode == "normal") {
e.cancelBubble = true
e.stopImmediatePropagation()
}
@@ -0,0 +1,61 @@
import Logger from "./logging"
This conversation was marked as resolved by saulrh

This comment has been minimized.

Copy link
@bovine3dom

bovine3dom Sep 1, 2018

Member

This file should probably be called state_content.ts.

This comment has been minimized.

Copy link
@saulrh

saulrh Sep 1, 2018

Author Contributor

Done, whoops.

const logger = new Logger("state")

export type ModeName =
| "normal"
| "insert"
| "hint"
| "ignore"
| "gobble"
| "input"
| "find"

export class PrevInput {
inputId: string
tab: number
jumppos?: number
}

class State {
mode: ModeName = "normal"
cmdHistory: string[] = []
prevInputs: PrevInput[] = [
{
inputId: undefined,
tab: undefined,
jumppos: undefined,
},
]
}

export type ContentStateProperty =
| "mode"
| "cmdHistory"
| "prevInputs"

export type ContentStateChangedCallback =
(property: ContentStateProperty, oldValue: any, newValue: any) => void

const onChangedListeners: ContentStateChangedCallback[] = []

export function addContentStateChangedListener(callback: ContentStateChangedCallback) {
onChangedListeners.push(callback)
}

export const contentState = (new Proxy({"mode": "normal"}, {
get: function(target, property: ContentStateProperty) {
return target[property]
},

set: function(target, property: ContentStateProperty, newValue) {
logger.debug("Content state changed!", property, newValue)

const oldValue = target[property]
target[property] = newValue

for (let listener of onChangedListeners) {
listener(property, oldValue, newValue)
}
return true
},
}) as any) as State
@@ -0,0 +1,39 @@
import { parser as exmode_parser } from "./parsers/exmode"
import { repeat } from "./.excmds_background.generated"

import Logger from "./logging"

const logger = new Logger("controller")

export let last_ex_str = ""

/** Parse and execute ExCmds */
// TODO(saulrh): replace this with messaging to the background
export async function acceptExCmd(exstr: string): Promise<any> {
// TODO: Errors should go to CommandLine.
try {
let [func, args] = exmode_parser(exstr)
// Stop the repeat excmd from recursing.
if (func !== repeat) last_ex_str = exstr
try {
return await func(...args)
} catch (e) {
// Errors from func are caught here (e.g. no next tab)
logger.error("background_controller: ", e)
}
} catch (e) {
// Errors from parser caught here
logger.error("background_controller: ", e)
}
}


import * as Messaging from "./messaging"

// Get messages from content
Messaging.addListener(
"controller_background",
Messaging.attributeCaller({
acceptExCmd,
}),
)
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.