mirror of
https://github.com/vale981/tridactyl
synced 2025-03-06 01:51:40 -05:00
Merge pull request #953 from saulrh/autocontain-coexistence
Autocontain coexistence
This commit is contained in:
commit
8e344b7c12
7 changed files with 423 additions and 66 deletions
|
@ -56,5 +56,7 @@ Since Tridactyl aims to provide all the features Vimperator and Pentadactyl had,
|
|||
- This is needed for Tridactyl to be able to go back to normal mode every time you open a new page. In the future we may use it for autocommands.
|
||||
- Read the text of all open tabs
|
||||
- This allows us to use Firefox's built-in find-in-page API, for, for example, allowing you to bind find-next and find-previous to `n` and `N`.
|
||||
- Monitor extension usage and manage themes:
|
||||
- Tridactyl needs this to integrate with and avoid conflicts with other extensions. For example, Tridactyl's contextual identity features use this to cooperate with the Multi-Account Containers extension.
|
||||
|
||||
[betas]: https://tridactyl.cmcaine.co.uk/betas/?sort=time&order=desc
|
||||
|
|
|
@ -19,6 +19,7 @@ import * as native from "@src/lib/native"
|
|||
import state from "@src/state"
|
||||
import * as webext from "@src/lib/webext"
|
||||
import { AutoContain } from "@src/lib/autocontainers"
|
||||
import * as extension_info from "@src/lib/extension_info"
|
||||
/* tslint:disable:import-spacing */
|
||||
; (window as any).tri = Object.assign(Object.create(null), {
|
||||
messaging,
|
||||
|
@ -133,22 +134,20 @@ browser.tabs.onActivated.addListener(ev => {
|
|||
|
||||
// {{{ AUTOCONTAINERS
|
||||
|
||||
extension_info.init()
|
||||
|
||||
const aucon = new AutoContain()
|
||||
|
||||
function cancelReq(details) {
|
||||
if (aucon.getCancelledRequest(details.tabId)) {
|
||||
aucon.clearCancelledRequests(details.tabId)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle cancelled requests as a result of autocontain.
|
||||
browser.webRequest.onCompleted.addListener(cancelReq,
|
||||
{ urls: ["<all_urls"], types: ["main_frame"] },
|
||||
)
|
||||
browser.webRequest.onCompleted.addListener(aucon.completedRequestListener, {
|
||||
urls: ["<all_urls>"],
|
||||
types: ["main_frame"],
|
||||
})
|
||||
|
||||
browser.webRequest.onErrorOccurred.addListener(cancelReq,
|
||||
{ urls: ["<all_urls>"], types: ["main_frame"] },
|
||||
)
|
||||
browser.webRequest.onErrorOccurred.addListener(aucon.completedRequestListener, {
|
||||
urls: ["<all_urls>"],
|
||||
types: ["main_frame"],
|
||||
})
|
||||
|
||||
// Contain autocmd.
|
||||
browser.webRequest.onBeforeRequest.addListener(
|
||||
|
@ -156,6 +155,11 @@ browser.webRequest.onBeforeRequest.addListener(
|
|||
{ urls: ["<all_urls>"], types: ["main_frame"] },
|
||||
["blocking"],
|
||||
)
|
||||
|
||||
browser.tabs.onCreated.addListener(
|
||||
aucon.tabCreatedListener,
|
||||
)
|
||||
|
||||
// }}}
|
||||
|
||||
// {{{ PERFORMANCE LOGGING
|
||||
|
|
|
@ -78,6 +78,7 @@ import * as UrlUtil from "@src/lib/url_util"
|
|||
import * as config from "@src/lib/config"
|
||||
import * as aliases from "@src/lib/aliases"
|
||||
import * as Logging from "@src/lib/logging"
|
||||
import { AutoContain } from "@src/lib/autocontainers"
|
||||
/** @hidden */
|
||||
const logger = new Logging.Logger("excmds")
|
||||
import * as CSS from "css"
|
||||
|
@ -1865,7 +1866,7 @@ export async function tabprev(increment = 1) {
|
|||
|
||||
/** Like [[open]], but in a new tab. If no address is given, it will open the newtab page, which can be set with `set newtab [url]`
|
||||
|
||||
Use the `-c` flag followed by a container name to open a tab in said container. Tridactyl will try to fuzzy match a name if an exact match is not found.
|
||||
Use the `-c` flag followed by a container name to open a tab in said container. Tridactyl will try to fuzzy match a name if an exact match is not found. If any autocontainer directives are configured and -c is not set, Tridactyl will try to use the right container automatically using your configurations.
|
||||
Use the `-b` flag to open the tab in the background.
|
||||
These two can be combined in any order, but need to be placed as the first arguments.
|
||||
|
||||
|
@ -1888,6 +1889,8 @@ export async function tabopen(...addressarr: string[]) {
|
|||
let active
|
||||
let container
|
||||
|
||||
const win = await browser.windows.getCurrent()
|
||||
|
||||
// Lets us pass both -b and -c in no particular order as long as they are up front.
|
||||
async function argParse(args): Promise<string[]> {
|
||||
if (args[0] === "-b") {
|
||||
|
@ -1896,7 +1899,6 @@ export async function tabopen(...addressarr: string[]) {
|
|||
argParse(args)
|
||||
} else if (args[0] === "-c") {
|
||||
// Ignore the -c flag if incognito as containers are disabled.
|
||||
const win = await browser.windows.getCurrent()
|
||||
if (!win.incognito) container = await Container.fuzzyMatch(args[1])
|
||||
else logger.error("[tabopen] can't open a container in a private browsing window.")
|
||||
|
||||
|
@ -1914,6 +1916,15 @@ export async function tabopen(...addressarr: string[]) {
|
|||
return nativeopen(address)
|
||||
}
|
||||
|
||||
const aucon = new AutoContain()
|
||||
if (!container && aucon.autocontainConfigured()) {
|
||||
const autoContainer = await aucon.getAuconForUrl(address)
|
||||
if (autoContainer && autoContainer !== "firefox-default") {
|
||||
container = autoContainer
|
||||
logger.debug("tabopen setting container automatically using autocontain directive")
|
||||
}
|
||||
}
|
||||
|
||||
return activeTabContainerId().then(containerId => {
|
||||
const args = { active } as any
|
||||
// Ensure -c has priority.
|
||||
|
@ -3158,6 +3169,8 @@ export function autocmd(event: string, url: string, ...excmd: string[]) {
|
|||
* * 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`.
|
||||
*
|
||||
* This *should* now peacefully coexist with the Temporary Containers and Multi-Account Containers addons. Do not trust this claim. If a fight starts the participants will try to open infinite tabs. It is *strongly* recommended that you use a tridactylrc so that you can abort a sorceror's-apprentice scenario by killing firefox, commenting out all of autocontainer directives in your rc file, and restarting firefox to clean up the mess. There are a number of strange behaviors resulting from limited coordination between extensions. Redirects can be particularly surprising; for example, with `:autocontain will-redirect.example.org example` set and `will-redirect.example.org` redirecting to `redirected.example.org`, navigating to `will-redirect.example.org` will result in the new tab being in the `example` container under some conditions and in the `firefox-default` container under others.
|
||||
*
|
||||
* @param domain The domain which will trigger the autoContain directive. Includes all subdomains.
|
||||
* @param container The container to open the url in.
|
||||
*/
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
* 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/
|
||||
|
||||
|
@ -27,89 +25,118 @@
|
|||
import * as Config from "@src/lib/config"
|
||||
import * as Container from "@src/lib/containers"
|
||||
import * as Logging from "@src/lib/logging"
|
||||
import * as ExtensionInfo from "@src/lib/extension_info"
|
||||
|
||||
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
|
||||
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
|
||||
clearCancelledRequests(tabId: number): void
|
||||
getCancelledRequest(tabId: number): ICancelledRequest
|
||||
parseAucons(details: IDetails): Promise<string>
|
||||
completedRequestListener(details: browser.webRequest.IDetails): void
|
||||
autocontainConfigured(): boolean
|
||||
getAuconForUrl(url: string): Promise<string>
|
||||
getAuconForDetails(details: browser.webRequest.IDetails): Promise<string>
|
||||
}
|
||||
|
||||
export class AutoContain implements IAutoContain {
|
||||
private cancelledRequests: ICancelledRequest[] = []
|
||||
private lastCreatedTab = null
|
||||
|
||||
autoContain = async (
|
||||
details: IDetails,
|
||||
): Promise<browser.webRequest.BlockingResponse> => {
|
||||
tabCreatedListener = (tab) => {
|
||||
this.lastCreatedTab = tab
|
||||
}
|
||||
|
||||
completedRequestListener = (details: browser.webRequest.IDetails) => {
|
||||
if (this.getCancelledRequest(details.tabId)) {
|
||||
this.clearCancelledRequests(details.tabId)
|
||||
}
|
||||
}
|
||||
|
||||
autocontainConfigured = (): boolean => {
|
||||
// No autocontain directives, no nothing.
|
||||
const aucons = Config.get("autocontain")
|
||||
if (Object.keys(aucons).length === 0) return { cancel: false }
|
||||
return Object.keys(aucons).length !== 0
|
||||
}
|
||||
|
||||
// Do not handle private tabs or invalid tabIds.
|
||||
if (details.tabId === -1) return { cancel: false }
|
||||
const tab = await browser.tabs.get(details.tabId)
|
||||
if (tab.incognito) return { cancel: false }
|
||||
autoContain = async (
|
||||
details: browser.webRequest.IDetails,
|
||||
): Promise<browser.webRequest.BlockingResponse> => {
|
||||
if (!this.autocontainConfigured()) return { cancel: false }
|
||||
|
||||
// Only handle http requests.
|
||||
if (details.url.search("^https?://") < 0) return { cancel: false }
|
||||
|
||||
const cookieStoreId = await this.parseAucons(details)
|
||||
// Do not handle invalid tabIds.
|
||||
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),
|
||||
],
|
||||
)
|
||||
|
||||
// If any other extensions claim this request, we'll ignore it and let them handle it.
|
||||
if (otherExtensionHasPriority) return { cancel: false }
|
||||
|
||||
// Do not handle private tabs.
|
||||
if (tab.incognito) return { cancel: false }
|
||||
|
||||
// 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({
|
||||
// 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)
|
||||
|
||||
// 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({
|
||||
url: details.url,
|
||||
cookieStoreId,
|
||||
active: tab.active,
|
||||
windowId: tab.windowId,
|
||||
index: tab.index + 1,
|
||||
openerTabId,
|
||||
}).then(result => {
|
||||
logger.debug("Autocontainer created tab %o", result)
|
||||
})
|
||||
.then(_ => {
|
||||
if (details.originUrl) {
|
||||
window.history.back()
|
||||
} else {
|
||||
browser.tabs.remove(details.tabId)
|
||||
}
|
||||
})
|
||||
|
||||
if (removeTab) {
|
||||
logger.debug("Closing newly-opened tab %o", tab)
|
||||
browser.tabs.remove(tab.id)
|
||||
}
|
||||
|
||||
return { cancel: true }
|
||||
}
|
||||
|
||||
// Handles the requests after the initial checks made in this.autoContain.
|
||||
cancelEarly = (tab: browser.tabs.Tab, details: IDetails): boolean => {
|
||||
cancelEarly = (
|
||||
tab: browser.tabs.Tab,
|
||||
details: browser.webRequest.IDetails,
|
||||
): boolean => {
|
||||
if (!this.cancelledRequests[tab.id]) {
|
||||
this.cancelRequest(tab, details)
|
||||
} else {
|
||||
|
@ -130,7 +157,10 @@ export class AutoContain implements IAutoContain {
|
|||
return false
|
||||
}
|
||||
|
||||
cancelRequest = (tab: browser.tabs.Tab, details: IDetails): void => {
|
||||
cancelRequest = (
|
||||
tab: browser.tabs.Tab,
|
||||
details: browser.webRequest.IDetails,
|
||||
): void => {
|
||||
this.cancelledRequests[tab.id] = {
|
||||
requestIds: {
|
||||
[details.requestId]: true,
|
||||
|
@ -158,12 +188,100 @@ export class AutoContain implements IAutoContain {
|
|||
}
|
||||
}
|
||||
|
||||
// Parses autocontain directives and returns valid cookieStoreIds or errors.
|
||||
parseAucons = async (details): Promise<string> => {
|
||||
// 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<boolean> => {
|
||||
// The checks for each extension can be done in parallel.
|
||||
const priorities = await Promise.all([
|
||||
this.checkMACPriority(details),
|
||||
this.checkTempContainersPriority(details),
|
||||
])
|
||||
return priorities.some(t => t)
|
||||
}
|
||||
|
||||
checkMACPriority = async (
|
||||
details: browser.webRequest.IDetails,
|
||||
): Promise<boolean> => {
|
||||
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",
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
// Do not handle urls that are claimed by the multi-account
|
||||
// containers extension. Code from
|
||||
// https://github.com/mozilla/multi-account-containers/wiki/API
|
||||
const macAssignment = await browser.runtime
|
||||
.sendMessage(
|
||||
ExtensionInfo.KNOWN_EXTENSIONS.multi_account_containers,
|
||||
{
|
||||
method: "getAssignment",
|
||||
url: details.url,
|
||||
},
|
||||
)
|
||||
.catch(error => {
|
||||
logger.warning("failed to communicate with multi-account containers extension: %o",
|
||||
error)
|
||||
return false
|
||||
})
|
||||
|
||||
if (macAssignment) {
|
||||
logger.debug(
|
||||
"multi-account containers extension has priority over autocontainer directives",
|
||||
)
|
||||
return true
|
||||
} else {
|
||||
logger.debug(
|
||||
"multi-account containers extension exists but does not claim priority",
|
||||
)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
checkTempContainersPriority = async (
|
||||
details: browser.webRequest.IDetails,
|
||||
): Promise<boolean> => {
|
||||
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",
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
// Anything that we'd contain in the default container will be
|
||||
// handed to the temp containers extension
|
||||
const willContainInDefault =
|
||||
(await this.getAuconForDetails(details)) === "firefox-default"
|
||||
if (willContainInDefault) {
|
||||
logger.info(
|
||||
"temporary containers extension has priority over autocontainer directives",
|
||||
)
|
||||
} else {
|
||||
logger.debug(
|
||||
"temporary containers extension exists but does not claim priority",
|
||||
)
|
||||
}
|
||||
return willContainInDefault
|
||||
}
|
||||
|
||||
getAuconForUrl = async (url: string): Promise<string> => {
|
||||
const aucons = Config.get("autocontain")
|
||||
const ausites = Object.keys(aucons)
|
||||
const aukeyarr = ausites.filter(
|
||||
e => details.url.search("^https?://[^/]*" + e + "/") >= 0,
|
||||
e => url.search("^https?://[^/]*" + e + "/") >= 0,
|
||||
)
|
||||
if (aukeyarr.length > 1) {
|
||||
logger.error(
|
||||
|
@ -186,4 +304,11 @@ export class AutoContain implements IAutoContain {
|
|||
return Container.getId(aucons[aukeyarr[0]])
|
||||
}
|
||||
}
|
||||
|
||||
// Parses autocontain directives and returns valid cookieStoreIds or errors.
|
||||
getAuconForDetails = async (
|
||||
details: browser.webRequest.IDetails,
|
||||
): Promise<string> => {
|
||||
return this.getAuconForUrl(details.url)
|
||||
}
|
||||
}
|
||||
|
|
77
src/lib/extension_info.ts
Normal file
77
src/lib/extension_info.ts
Normal file
|
@ -0,0 +1,77 @@
|
|||
/** Extensions and compatibility
|
||||
|
||||
Looks us and communicates with other installed extensions so we can
|
||||
be compatible with them.
|
||||
|
||||
*/
|
||||
|
||||
/** Friendly-names of extensions that are used in different places so
|
||||
that we can refer to them with more readable and less magic ids.
|
||||
*/
|
||||
export const KNOWN_EXTENSIONS: { [name: string]: string } = {
|
||||
temp_containers: "{c607c8df-14a7-4f28-894f-29e8722976af}",
|
||||
multi_account_containers: "@testpilot-containers",
|
||||
}
|
||||
|
||||
/** List of currently installed extensions.
|
||||
*/
|
||||
const installedExtensions: {
|
||||
[id: string]: browser.management.IExtensionInfo
|
||||
} = {}
|
||||
|
||||
function updateExtensionInfo(
|
||||
extension: browser.management.IExtensionInfo,
|
||||
): void {
|
||||
installedExtensions[extension.id] = extension
|
||||
}
|
||||
|
||||
export function getExtensionEnabled(id: string): boolean {
|
||||
if (getExtensionInstalled(id)) {
|
||||
return installedExtensions[id].enabled
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export function getExtensionInstalled(id: string): boolean {
|
||||
return id in installedExtensions
|
||||
}
|
||||
|
||||
async function hasManagementPermission() {
|
||||
return browser.permissions.contains({
|
||||
"permissions": ["management"],
|
||||
})
|
||||
}
|
||||
|
||||
/** Read installed extensions to populate the list at startup time.
|
||||
*/
|
||||
export async function init() {
|
||||
// If we don't have the permission, bail out. Our list of
|
||||
// installed extensions will be left uninitialized, so all of our
|
||||
// external interfaces will pretend that no other extensions
|
||||
// exist. This SHOULD result in tridactyl acting the same as it
|
||||
// did before the extension interoperability feature was added in
|
||||
// the first place, which isn't a great loss.
|
||||
const hasPermission = await hasManagementPermission()
|
||||
if (!hasPermission) {
|
||||
return
|
||||
}
|
||||
|
||||
// Code borrowed from
|
||||
// https://github.com/stoically/temporary-containers/blob/master/src/background/management.js
|
||||
let extensions = []
|
||||
try {
|
||||
extensions = await browser.management.getAll()
|
||||
} catch (e) {
|
||||
return
|
||||
}
|
||||
|
||||
for (const extension of extensions) {
|
||||
installedExtensions[extension.id] = extension
|
||||
}
|
||||
|
||||
browser.management.onInstalled.addListener(updateExtensionInfo)
|
||||
browser.management.onEnabled.addListener(updateExtensionInfo)
|
||||
browser.management.onDisabled.addListener(updateExtensionInfo)
|
||||
browser.management.onUninstalled.addListener(updateExtensionInfo)
|
||||
}
|
|
@ -4,7 +4,7 @@ import Logger from "@src/lib/logging"
|
|||
|
||||
const logger = new Logger("requests")
|
||||
|
||||
class DefaultMap extends Map {
|
||||
class DefaultMap<K, V> extends Map<K, V> {
|
||||
constructor(private defaultFactory, ...args) {
|
||||
// super(...args)
|
||||
super()
|
||||
|
@ -36,7 +36,7 @@ export function clobberCSP(response) {
|
|||
)
|
||||
|
||||
if (cspHeader !== undefined) {
|
||||
const policy = new DefaultMap(
|
||||
const policy = new DefaultMap<string, Set<string>>(
|
||||
() => new Set(),
|
||||
csp.parse(cspHeader.value),
|
||||
)
|
||||
|
|
136
src/tridactyl.d.ts
vendored
136
src/tridactyl.d.ts
vendored
|
@ -87,6 +87,142 @@ declare namespace browser.tabs {
|
|||
function toggleReaderMode(tabId?: number): Promise<void>
|
||||
}
|
||||
|
||||
// web-ext-browser barely declares a third of the management
|
||||
// interface, and we can't switch to @types/firefox-webext-browser yet
|
||||
// because their enums are all messed up (see
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/pull/28369)
|
||||
// Instead, we'll copy-paste as much as we need from the fixed branch:
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d1180e5218a7bf69e6f0da5ac2e2584bd57a1cdf/types/firefox-webext-browser/index.d.ts
|
||||
interface WebExtEventBase<
|
||||
TAddListener extends (...args: any[]) => any,
|
||||
TCallback
|
||||
> {
|
||||
addListener: TAddListener
|
||||
|
||||
removeListener(cb: TCallback): void
|
||||
|
||||
hasListener(cb: TCallback): boolean
|
||||
}
|
||||
type WebExtEvent<TCallback extends (...args: any[]) => any> = WebExtEventBase<
|
||||
(callback: TCallback) => void,
|
||||
TCallback
|
||||
>
|
||||
declare namespace browser.management {
|
||||
/* management types */
|
||||
|
||||
/** Information about an icon belonging to an extension. */
|
||||
interface IconInfo {
|
||||
/**
|
||||
* A number representing the width and height of the icon. Likely values include (but are not limited to) 128,
|
||||
* 48, 24, and 16.
|
||||
*/
|
||||
size: number
|
||||
/**
|
||||
* The URL for this icon image. To display a grayscale version of the icon (to indicate that an extension is
|
||||
* disabled, for example), append `?grayscale=true` to the URL.
|
||||
*/
|
||||
url: string
|
||||
}
|
||||
|
||||
/** A reason the item is disabled. */
|
||||
type ExtensionDisabledReason = "unknown" | "permissions_increase"
|
||||
|
||||
/** The type of this extension. Will always be 'extension'. */
|
||||
type ExtensionType = "extension" | "theme"
|
||||
|
||||
/**
|
||||
* How the extension was installed. One of
|
||||
* `development`: The extension was loaded unpacked in developer mode,
|
||||
* `normal`: The extension was installed normally via an .xpi file,
|
||||
* `sideload`: The extension was installed by other software on the machine,
|
||||
* `other`: The extension was installed by other means.
|
||||
*/
|
||||
type ExtensionInstallType = "development" | "normal" | "sideload" | "other"
|
||||
|
||||
/** Information about an installed extension. */
|
||||
interface IExtensionInfo {
|
||||
/** The extension's unique identifier. */
|
||||
id: string
|
||||
/** The name of this extension. */
|
||||
name: string
|
||||
/** A short version of the name of this extension. */
|
||||
shortName?: string
|
||||
/** The description of this extension. */
|
||||
description: string
|
||||
/** The version of this extension. */
|
||||
version: string
|
||||
/** The version name of this extension if the manifest specified one. */
|
||||
versionName?: string
|
||||
/** Whether this extension can be disabled or uninstalled by the user. */
|
||||
mayDisable: boolean
|
||||
/** Whether it is currently enabled or disabled. */
|
||||
enabled: boolean
|
||||
/** A reason the item is disabled. */
|
||||
disabledReason?: ExtensionDisabledReason
|
||||
/** The type of this extension. Will always return 'extension'. */
|
||||
type: ExtensionType
|
||||
/** The URL of the homepage of this extension. */
|
||||
homepageUrl?: string
|
||||
/** The update URL of this extension. */
|
||||
updateUrl?: string
|
||||
/** The url for the item's options page, if it has one. */
|
||||
optionsUrl: string
|
||||
/**
|
||||
* A list of icon information. Note that this just reflects what was declared in the manifest, and the actual
|
||||
* image at that url may be larger or smaller than what was declared, so you might consider using explicit
|
||||
* width and height attributes on img tags referencing these images. See the manifest documentation on icons
|
||||
* for more details.
|
||||
*/
|
||||
icons?: IconInfo[]
|
||||
/** Returns a list of API based permissions. */
|
||||
permissions?: string[]
|
||||
/** Returns a list of host based permissions. */
|
||||
hostPermissions?: string[]
|
||||
/** How the extension was installed. */
|
||||
installType: ExtensionInstallType
|
||||
}
|
||||
|
||||
/* management functions */
|
||||
/** Returns a list of information about installed extensions. */
|
||||
function getAll(): Promise<IExtensionInfo[] | undefined>
|
||||
|
||||
/* management events */
|
||||
/** Fired when an addon has been disabled. */
|
||||
const onDisabled: WebExtEvent<(info: IExtensionInfo) => void>
|
||||
|
||||
/** Fired when an addon has been enabled. */
|
||||
const onEnabled: WebExtEvent<(info: IExtensionInfo) => void>
|
||||
|
||||
/** Fired when an addon has been installed. */
|
||||
const onInstalled: WebExtEvent<(info: IExtensionInfo) => void>
|
||||
|
||||
/** Fired when an addon has been uninstalled. */
|
||||
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,
|
||||
|
|
Loading…
Add table
Reference in a new issue