diff --git a/package.json b/package.json index 778c6f8c..3d6e777f 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "typedoc-default-themes": "^0.10.2" }, "devDependencies": { + "@types/firefox-webext-browser": "^78.0.0", "@types/jest": "^26.0.3", "@types/node": "^14.0.14", "@types/selenium-webdriver": "^4.0.9", diff --git a/src/background.ts b/src/background.ts index 19cec122..dc9b0a13 100644 --- a/src/background.ts +++ b/src/background.ts @@ -89,7 +89,7 @@ browser.webNavigation.onDOMContentLoaded.addListener(() => { // Prevent Tridactyl from being updated while it is running in the hope of fixing #290 browser.runtime.onUpdateAvailable.addListener(_ => undefined) -browser.runtime.onStartup.addListener(_ => { +browser.runtime.onStartup.addListener(() => { config.getAsync("autocmds", "TriStart").then(aucmds => { const hosts = Object.keys(aucmds) // If there's only one rule and it's "all", no need to check the hostname diff --git a/src/completions/History.ts b/src/completions/History.ts index a1ef6d25..b6e00837 100644 --- a/src/completions/History.ts +++ b/src/completions/History.ts @@ -17,14 +17,12 @@ class HistoryCompletionOption extends Completions.CompletionOptionHTML // Create HTMLElement this.html = html` - ${"".padEnd(2)} - ${page.title} - - ${page.url} - - ` + ${"".padEnd(2)} + ${page.title} + + ${page.url} + + ` } } @@ -78,8 +76,13 @@ export class HistoryCompletionSource extends Completions.CompletionSourceFuse { options += options ? " " : "" // Options are pre-trimmed to the right length. - this.options = (await this.scoreOptions(query, config.get("historyresults"))).map( - page => new HistoryCompletionOption(options + page.url, page as any), + // Typescript throws an error here - further investigation is probably warranted + this.options = ((await this.scoreOptions( + query, + config.get("historyresults"), + )) as any).map( + page => + new HistoryCompletionOption(options + page.url, page), ) // Deselect any selected, but remember what they were. @@ -88,9 +91,13 @@ export class HistoryCompletionSource extends Completions.CompletionSourceFuse { // Set initial state to normal, unless the option was selected a moment // ago, then reselect it so that users don't lose their selections. - this.options.forEach(option => option.state = "normal") + this.options.forEach(option => (option.state = "normal")) for (const option of this.options) { - if (lastFocused !== undefined && lastFocused.value === option.value && prevStr.length <= exstr.length) { + if ( + lastFocused !== undefined && + lastFocused.value === option.value && + prevStr.length <= exstr.length + ) { this.select(option) break } @@ -103,7 +110,6 @@ export class HistoryCompletionSource extends Completions.CompletionSourceFuse { // eslint-disable-next-line @typescript-eslint/no-empty-function updateChain() {} - private async scoreOptions(query: string, n: number) { if (!query || config.get("historyresults") === 0) { return (await providers.getTopSites()).slice(0, n) diff --git a/src/completions/Window.ts b/src/completions/Window.ts index 9cc3f498..1abd6682 100644 --- a/src/completions/Window.ts +++ b/src/completions/Window.ts @@ -5,7 +5,7 @@ class WindowCompletionOption extends Completions.CompletionOptionHTML implements Completions.CompletionOptionFuse { public fuseKeys = [] - constructor(win) { + constructor(win: browser.windows.Window) { super() this.value = win.id this.fuseKeys.push(`${win.title}`) diff --git a/src/lib/autocontainers.ts b/src/lib/autocontainers.ts index 7b6aa9ce..1e274929 100644 --- a/src/lib/autocontainers.ts +++ b/src/lib/autocontainers.ts @@ -35,32 +35,26 @@ interface ICancelledRequest { } interface IAutoContain { - autoContain(details: browser.webRequest.IDetails): any - cancelEarly( - tab: browser.tabs.Tab, - details: browser.webRequest.IDetails, - ): boolean - cancelRequest( - tab: browser.tabs.Tab, - details: browser.webRequest.IDetails, - ): void + autoContain(details): any + cancelEarly(tab: browser.tabs.Tab, details): boolean + cancelRequest(tab: browser.tabs.Tab, details): void clearCancelledRequests(tabId: number): void getCancelledRequest(tabId: number): ICancelledRequest - completedRequestListener(details: browser.webRequest.IDetails): void + completedRequestListener(details): void autocontainConfigured(): boolean getAuconForUrl(url: string): Promise - getAuconForDetails(details: browser.webRequest.IDetails): Promise + getAuconForDetails(details): Promise } export class AutoContain implements IAutoContain { private cancelledRequests: ICancelledRequest[] = [] private lastCreatedTab = null - tabCreatedListener = (tab) => { + tabCreatedListener = tab => { this.lastCreatedTab = tab } - completedRequestListener = (details: browser.webRequest.IDetails) => { + completedRequestListener = details => { if (this.getCancelledRequest(details.tabId)) { this.clearCancelledRequests(details.tabId) } @@ -73,12 +67,13 @@ export class AutoContain implements IAutoContain { } autoContain = async ( - details: browser.webRequest.IDetails, + details, ): Promise => { if (!this.autocontainConfigured()) return { cancel: false } // Only handle in strict mode. - if (Config.get("autocontainmode") === "relaxed") return { cancel: false } + if (Config.get("autocontainmode") === "relaxed") + return { cancel: false } // Only handle http requests. if (details.url.search("^https?://") < 0) return { cancel: false } @@ -87,13 +82,15 @@ export class AutoContain implements IAutoContain { if (details.tabId === -1) return { cancel: false } // Do all of our async lookups in parallel. - const [tab, otherExtensionHasPriority, cookieStoreId] = await Promise.all( - [ - browser.tabs.get(details.tabId), - this.checkOtherExtensionsHavePriority(details), - this.getAuconForDetails(details), - ], - ) + const [ + tab, + otherExtensionHasPriority, + cookieStoreId, + ] = await Promise.all([ + browser.tabs.get(details.tabId), + this.checkOtherExtensionsHavePriority(details), + this.getAuconForDetails(details), + ]) // If any other extensions claim this request, we'll ignore it and let them handle it. if (otherExtensionHasPriority) return { cancel: false } @@ -108,22 +105,30 @@ export class AutoContain implements IAutoContain { // If this navigation created a tab, we cancel and then kill // the newly-created tab after opening. - const removeTab = this.lastCreatedTab && (this.lastCreatedTab.id === tab.id) + const removeTab = + this.lastCreatedTab && this.lastCreatedTab.id === tab.id // Figure out which tab should be the parent of the tab we'll // be creating in the selected container. const openerTabId = removeTab ? tab.openerTabId : tab.id - logger.debug("in tab %o and with details %o, reopening from container %o to container %o", - tab, details, tab.cookieStoreId, cookieStoreId) - browser.tabs.create({ + logger.debug( + "in tab %o and with details %o, reopening from container %o to container %o", + tab, + details, + tab.cookieStoreId, + cookieStoreId, + ) + browser.tabs + .create({ url: details.url, cookieStoreId, active: tab.active, windowId: tab.windowId, index: tab.index + 1, openerTabId, - }).then(result => { + }) + .then(result => { logger.debug("Autocontainer created tab %o", result) }) @@ -136,10 +141,7 @@ export class AutoContain implements IAutoContain { } // Handles the requests after the initial checks made in this.autoContain. - cancelEarly = ( - tab: browser.tabs.Tab, - details: browser.webRequest.IDetails, - ): boolean => { + cancelEarly = (tab: browser.tabs.Tab, details): boolean => { if (!this.cancelledRequests[tab.id]) { this.cancelRequest(tab, details) } else { @@ -160,10 +162,7 @@ export class AutoContain implements IAutoContain { return false } - cancelRequest = ( - tab: browser.tabs.Tab, - details: browser.webRequest.IDetails, - ): void => { + cancelRequest = (tab: browser.tabs.Tab, details): void => { this.cancelledRequests[tab.id] = { requestIds: { [details.requestId]: true, @@ -180,7 +179,8 @@ export class AutoContain implements IAutoContain { }, 2000) } - getCancelledRequest = (tabId: number): ICancelledRequest => this.cancelledRequests[tabId] + getCancelledRequest = (tabId: number): ICancelledRequest => + this.cancelledRequests[tabId] // Clear the cancelled requests. clearCancelledRequests = (tabId: number): void => { @@ -191,9 +191,7 @@ export class AutoContain implements IAutoContain { // Checks to see if there are any other container-related extensions and avoids getting into // fights with them. - checkOtherExtensionsHavePriority = async ( - details: browser.webRequest.IDetails, - ): Promise => { + checkOtherExtensionsHavePriority = async (details): Promise => { // The checks for each extension can be done in parallel. const priorities = await Promise.all([ this.checkMACPriority(details), @@ -202,18 +200,14 @@ export class AutoContain implements IAutoContain { return priorities.some(t => t) } - checkMACPriority = async ( - details: browser.webRequest.IDetails, - ): Promise => { + checkMACPriority = async (details): Promise => { if ( !ExtensionInfo.getExtensionEnabled( ExtensionInfo.KNOWN_EXTENSIONS.multi_account_containers, ) ) { // It can't take priority if it's not enabled. - logger.debug( - "multi-account containers extension does not exist", - ) + logger.debug("multi-account containers extension does not exist") return false } @@ -229,8 +223,10 @@ export class AutoContain implements IAutoContain { }, ) .catch(error => { - logger.warning("failed to communicate with multi-account containers extension: %o", - error) + logger.warning( + "failed to communicate with multi-account containers extension: %o", + error, + ) return false }) @@ -247,18 +243,14 @@ export class AutoContain implements IAutoContain { } } - checkTempContainersPriority = async ( - details: browser.webRequest.IDetails, - ): Promise => { + checkTempContainersPriority = async (details): Promise => { if ( !ExtensionInfo.getExtensionEnabled( ExtensionInfo.KNOWN_EXTENSIONS.temp_containers, ) ) { // It can't take priority if it's not enabled. - logger.debug( - "temporary containers extension does not exist", - ) + logger.debug("temporary containers extension does not exist") return false } @@ -281,9 +273,7 @@ export class AutoContain implements IAutoContain { getAuconForUrl = async (url: string): Promise => { const aucons = Config.get("autocontain") const ausites = Object.keys(aucons) - const aukeyarr = ausites.filter( - e => url.search(e) >= 0, - ) + const aukeyarr = ausites.filter(e => url.search(e) >= 0) if (aukeyarr.length > 1) { logger.error( "Too many autocontain directives match this url. Not containing.", @@ -307,7 +297,6 @@ export class AutoContain implements IAutoContain { } // Parses autocontain directives and returns valid cookieStoreIds or errors. - getAuconForDetails = async ( - details: browser.webRequest.IDetails, - ): Promise => this.getAuconForUrl(details.url) + getAuconForDetails = async (details): Promise => + this.getAuconForUrl(details.url) } diff --git a/src/lib/containers.ts b/src/lib/containers.ts index 9bfc170e..e54494ca 100644 --- a/src/lib/containers.ts +++ b/src/lib/containers.ts @@ -84,8 +84,8 @@ export function update( containerId: string, updateObj: { name: string - color: browser.contextualIdentities.IdentityColor - icon: browser.contextualIdentities.IdentityIcon + color: string + icon: string }, ) { const { name, color, icon } = updateObj @@ -147,8 +147,8 @@ export async function exists(cname: string): Promise { export function fromString(name: string, color: string, icon: string, id = "") { return { name, - color: color as browser.contextualIdentities.IdentityColor, - icon: icon as browser.contextualIdentities.IdentityIcon, + color, + icon, cookieStoreId: id, } as browser.contextualIdentities.ContextualIdentity // rules are made to be broken } diff --git a/src/lib/messaging.ts b/src/lib/messaging.ts index 576b5385..893b2945 100644 --- a/src/lib/messaging.ts +++ b/src/lib/messaging.ts @@ -69,7 +69,11 @@ export function attributeCaller(obj) { return handler } -interface TypedMessage { +interface TypedMessage< + Root, + Type extends keyof Root, + Command extends keyof Root[Type] +> { type: Type command: Command args: Parameters @@ -79,23 +83,80 @@ function backgroundHandler< Root, Type extends keyof Root, Command extends keyof Root[Type] - >(root: Root, - message: TypedMessage, - sender: browser.runtime.MessageSender, - ): ReturnType { +>( + root: Root, + message: TypedMessage, + sender: browser.runtime.MessageSender, +): ReturnType { return root[message.type][message.command](...message.args) } export function setupListener(root: Root) { - browser.runtime.onMessage.addListener((message: any, sender: browser.runtime.MessageSender) => { - if (message.type in root) { - if (!(message.command in root[message.type])) - throw new Error(`missing handler in protocol ${message.type} ${message.command}`) - if (!Array.isArray(message.args)) - throw new Error(`wrong arguments in protocol ${message.type} ${message.command}`) - return backgroundHandler(root, message, sender) - } - }); + // What part of + // + // ``` + // ERROR in /home/olie/projects/tridactyl/src/lib/messaging.ts + // ./src/lib/messaging.ts + // [tsl] ERROR in /home/olie/projects/tridactyl/src/lib/messaging.ts(90,43) + // TS2345: Argument of type '(message: any, sender: browser.runtime.MessageSender) => ReturnType' is not assignable to parameter of type '(message: any, sender: MessageSender, sendResponse: (response?: any) => void) => boolean | void | Promise'. + // Type 'ReturnType' is not assignable to type 'boolean | void | Promise'. + // Type 'unknown' is not assignable to type 'boolean | void | Promise'. + // Type 'unknown' is not assignable to type 'Promise'. + // Type 'ReturnType' is not assignable to type 'boolean | void | Promise'. + // Type 'unknown' is not assignable to type 'boolean | void | Promise'. + // Type 'unknown' is not assignable to type 'Promise'. + // Type 'ReturnType | ReturnType | ReturnType' is not assignable to type 'boolean | void | Promise'. + // Type 'ReturnType' is not assignable to type 'boolean | void | Promise'. + // Type 'unknown' is not assignable to type 'boolean | void | Promise'. + // Type 'unknown' is not assignable to type 'Promise'. + // Type 'ReturnType' is not assignable to type 'boolean | void | Promise'. + // Type 'unknown' is not assignable to type 'boolean | void | Promise'. + // Type 'unknown' is not assignable to type 'Promise'. + // Type 'ReturnType | ReturnType | ReturnType' is not assignable to type 'boolean | void | Promise'. + // Type 'ReturnType' is not assignable to type 'boolean | void | Promise'. + // Type 'unknown' is not assignable to type 'boolean | void | Promise'. + // Type 'unknown' is not assignable to type 'Promise'. + // Type 'ReturnType' is not assignable to type 'Promise'. + // Type 'ReturnType' is not assignable to type 'Promise'. + // Type 'ReturnType' is not assignable to type 'Promise'. + // Type 'ReturnType' is not assignable to type 'Promise'. + // Type 'ReturnType' is not assignable to type 'Promise'. + // Type 'unknown' is not assignable to type 'Promise'. + // Type 'ReturnType' is not assignable to type 'Promise'. + // Type 'unknown' is not assignable to type 'Promise'. + // Type 'ReturnType | ReturnType | ReturnType' is not assignable to type 'Promise'. + // Type 'ReturnType' is not assignable to type 'Promise'. + // Type 'unknown' is not assignable to type 'Promise'. + // Type 'ReturnType' is not assignable to type 'Promise'. + // Type 'unknown' is not assignable to type 'Promise'. + // Type 'ReturnType | ReturnType | ReturnType' is not assignable to type 'Promise'. + // Type 'ReturnType' is not assignable to type 'Promise'. + // Type '{}' is missing the following properties from type 'Promise': then, catch, [Symbol.toStringTag], finally + // + // ERROR in /home/olie/projects/tridactyl/src/lib/messaging.ts + // ./src/lib/messaging.ts + // [tsl] ERROR in /home/olie/projects/tridactyl/src/lib/messaging.ts(115,40) + // TS2558: Expected 0 type arguments, but got 2. + // ``` + // + // don't you understand? + // + // (This is why there is an `: any => `) + browser.runtime.onMessage.addListener( + (message: any, sender: browser.runtime.MessageSender): any => { + if (message.type in root) { + if (!(message.command in root[message.type])) + throw new Error( + `missing handler in protocol ${message.type} ${message.command}`, + ) + if (!Array.isArray(message.args)) + throw new Error( + `wrong arguments in protocol ${message.type} ${message.command}`, + ) + return backgroundHandler(root, message, sender) + } + }, + ) } type StripPromise = T extends Promise ? U : T @@ -105,14 +166,16 @@ export async function message< Type extends keyof Messages.Background, Command extends keyof Messages.Background[Type], F extends((...args: any) => any) & Messages.Background[Type][Command] - >(type: Type, command: Command, ...args: Parameters) { +>(type: Type, command: Command, ...args: Parameters) { const message: TypedMessage = { type, command, - args + args, } - return browser.runtime.sendMessage>>(message) + // Typescript didn't like this + // return browser.runtime.sendMessage>>(message) + return browser.runtime.sendMessage(message) } /** Message the active tab of the currentWindow */ @@ -124,7 +187,12 @@ export async function messageActiveTab( return messageTab(await activeTabId(), type, command, args) } -export async function messageTab(tabId, type: TabMessageType, command, args?): Promise { +export async function messageTab( + tabId, + type: TabMessageType, + command, + args?, +): Promise { const message: Message = { type, command, @@ -154,7 +222,10 @@ export async function messageAllTabs( responses.push(await messageTab(tab.id, type, command, args)) } catch (e) { // Skip errors caused by tabs we aren't running on - if (e.message != "Could not establish connection. Receiving end does not exist.") { + if ( + e.message != + "Could not establish connection. Receiving end does not exist." + ) { logger.error(e) } } diff --git a/src/tridactyl.d.ts b/src/tridactyl.d.ts index 3eb066fd..6607ecf5 100644 --- a/src/tridactyl.d.ts +++ b/src/tridactyl.d.ts @@ -186,29 +186,6 @@ declare namespace browser.management { const onUninstalled: WebExtEvent<(info: IExtensionInfo) => void> } -/** 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 - -*/ -declare namespace browser.webRequest { - interface IDetails { - // frameAncestors: any[] - frameId: number - method: string - originUrl: string - parentFrameId: number - proxyInfo?: any - requestBody?: any - requestId: string - tabId: number - timeStamp: number - type: ResourceType - url: string - } -} - // html-tagged-template.js declare function html( strings: TemplateStringsArray, diff --git a/tsconfig.json b/tsconfig.json index 98d43549..965e9dd1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,13 +7,13 @@ "sourceMap": true, "target": "es2017", "lib": ["es2017","es2019.array", "es2018.promise", "dom", "dom.iterable"], - "types": ["web-ext-types"], "experimentalDecorators": true, "alwaysStrict": true, "strictBindCallApply": true, "noImplicitThis": true, "strictFunctionTypes": true, "baseUrl": "src/", + "types": ["@types/firefox-webext-browser", "web-ext-types"], "paths": { "@src/*": ["*"] } diff --git a/yarn.lock b/yarn.lock index ffbaf6f3..6c27f7a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -672,6 +672,11 @@ resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== +"@types/firefox-webext-browser@^78.0.0": + version "78.0.0" + resolved "https://registry.yarnpkg.com/@types/firefox-webext-browser/-/firefox-webext-browser-78.0.0.tgz#9f175355beb3824dce8d5811e59585fba84dca6b" + integrity sha512-ExCtjncdA1ITEjEitvtjGcXslGPA/ZMxOxzMGfpLHZ6ZJAAx6itQi+ZEctgPFN6uPW4lSJalSsEC+lAz0Wkz9A== + "@types/graceful-fs@^4.1.2": version "4.1.3" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.3.tgz#039af35fe26bec35003e8d86d2ee9c586354348f"