26 |
27 |
28 | ## Table of Contents
29 |
30 | - [Table of Contents](#table-of-contents)
31 | - [Features](#features)
32 | - [How Chat Guard Work?](#how-chat-guard-work)
33 | - [License](#license)
34 | - [Donation](#donation)
35 |
36 | ## Features
37 |
38 | - End-to-End Encryption (E2E): Enjoy secure and private conversations without compromising your data.
39 |
40 | - Cross-Application Compatibility: While currently limited to Bale Messenger during the beta phase, ChatGuard aims to extend its support to a wide range of messaging applications.
41 |
42 | - Serverless: No need of server for exchanging public key,chatGuard uses the Messenger that running on as messaging service to transfer public keys.
43 |
44 | ## How Chat Guard Work?
45 |
46 | ChatGuard uses a hybrid encryption method that include a RSA handsake and after that sending every message as encrypted packet with unique secret key that encrypted by the user public key.
47 |
48 | - for more information read the documentation for [how it work](https://chat-guard.vercel.app/encryption/introduction)
49 |
50 | ## License
51 |
52 | Distributed under the Apache-2.0 License. See [Apache-2.0 license](https://github.com/PrivacyForge/ChatGuard/blob/main/LICENSE.md) for more information.
53 |
54 | ## Donation
55 |
56 | I appreciate every donation for this project
57 | every donation will be spend on growing the project and releasing it on different platform like safari, etc.
58 |
59 |
60 |
--------------------------------------------------------------------------------
/src/assets/icons/lock.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/content/scripts/listeners.ts:
--------------------------------------------------------------------------------
1 | import Cipher from "src/class/Cipher";
2 | import { config } from "src/config";
3 | import useListener from "src/hooks/useListener";
4 | import { chatStore } from "src/store/chat.store";
5 | import type { Url } from "src/store/url.store";
6 | import { getDeviceType } from "src/utils";
7 | import LocalStorage from "src/utils/LocalStorage";
8 | import logger from "src/utils/logger";
9 | import { clickTo, typeTo } from "src/utils/userAction";
10 | import { wait } from "src/utils/wait";
11 | import { makeElementInvisible, makeElementVisible } from "src/utils/elementVisibility";
12 | import { useConfig } from "src/hooks/useConfig";
13 |
14 | export const registerEventListener = (urlStore: Url) => {
15 | const cipher = new Cipher();
16 | const type = getDeviceType();
17 | const { getSelector } = useConfig();
18 | const isTouch = type === "mobile" ? true : false;
19 | const { onClick } = useListener();
20 |
21 | const handleSubmitClicked = async (e: Event) => {
22 | const contact = LocalStorage.getMap(config.CONTACTS_STORAGE_KEY, urlStore.id);
23 | let textFieldElement = document.querySelector(getSelector("textField")) as HTMLElement;
24 | const messageLengthIsOk = (textFieldElement.textContent || "").length <= 1200;
25 |
26 | if (!textFieldElement.textContent?.trim() || !contact.enable) return;
27 |
28 | e.preventDefault();
29 | e.stopImmediatePropagation();
30 |
31 | if (!messageLengthIsOk) return alert("character length should be bellow 1200 character");
32 |
33 | makeElementInvisible(textFieldElement);
34 | const encrypted = await cipher.createDRSAP(textFieldElement.textContent || "", urlStore.id);
35 | if (!encrypted) return makeElementVisible(textFieldElement);
36 |
37 | typeTo(getSelector("textField"), encrypted);
38 | await wait(25);
39 | chatStore.update((prev) => ({ ...prev, clickSubmit: true }));
40 | clickTo(getSelector("submitButton"));
41 | makeElementVisible(textFieldElement);
42 | textFieldElement.focus();
43 | logger.info("Message sent, Send button clicked");
44 | };
45 |
46 | const handleTextFieldKeyDown = async (e: KeyboardEvent) => {
47 | // check if we writing text to our textfield or not
48 | let isEqual = (e.target as HTMLElement).isEqualNode(document.querySelector(getSelector("textField")));
49 | if (!isEqual) return;
50 |
51 | const contact = LocalStorage.getMap(config.CONTACTS_STORAGE_KEY, urlStore.id);
52 | let textFieldElement = document.querySelector(getSelector("textField")) as HTMLElement;
53 | const messageLengthIsOk = (textFieldElement.textContent || "").length <= 1200;
54 |
55 | if (e.key !== "Enter" || !contact.enable || !textFieldElement.textContent?.trim() || e.shiftKey || isTouch) {
56 | return;
57 | }
58 | e.preventDefault();
59 | e.stopImmediatePropagation();
60 |
61 | if (!messageLengthIsOk) return alert("character length should be bellow 1200 character");
62 | makeElementInvisible(textFieldElement);
63 | const encrypted = await cipher.createDRSAP(textFieldElement.textContent || "", urlStore.id);
64 | if (!encrypted) return makeElementVisible(textFieldElement);
65 |
66 | typeTo(getSelector("textField"), encrypted);
67 | await wait(25);
68 | chatStore.update((prev) => ({ ...prev, submit: true }));
69 | clickTo(getSelector("submitButton"));
70 | makeElementVisible(textFieldElement);
71 | textFieldElement.focus();
72 | logger.info("Message sent, Form submitted");
73 | };
74 |
75 | document.addEventListener("keydown", handleTextFieldKeyDown, { capture: true });
76 | onClick(getSelector("submitButton"), handleSubmitClicked);
77 | };
78 |
--------------------------------------------------------------------------------
/src/components/modules/popup/Settings.svelte:
--------------------------------------------------------------------------------
1 |
34 |
35 |
36 |
51 |
52 |
53 |
54 |
Chat Guard
55 |
56 |
57 |
58 |
67 |
68 |
69 |
128 |
--------------------------------------------------------------------------------
/src/components/modules/content/LoadingScreen.svelte:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
27 | {#if !$state.loading && isShow}
28 |
46 |
Handshake Accepted
47 | {:else}
48 |
57 |
58 |
65 |
66 |
67 |
Waiting for Handshake
68 |
Cancel Waiting
69 | {/if}
70 |
71 |
72 |
73 |
129 |
--------------------------------------------------------------------------------
/src/components/modules/popup/AdvancedSetting.svelte:
--------------------------------------------------------------------------------
1 |
75 |
76 |
77 |
78 |
Config
79 |
80 |
The Config file includes your private key, public key, and Guard ID. You can export/import/reset your keys here
82 | and use them on other devices or for backups.
83 |
84 |
85 |
86 |
87 |
97 |
98 |
99 | Reset
100 |
101 | {#if error}
102 |
103 | {error}
104 |
105 | {/if}
106 |
107 |
108 |
109 |
158 |
--------------------------------------------------------------------------------
/src/config/index.ts:
--------------------------------------------------------------------------------
1 | import type { Config, Selector } from "src/types/Config";
2 |
3 | export const initLog = `
4 | ██████ ██ ██ █████ ████████
5 | ██ ██ ██ ██ ██ ██
6 | ██ ███████ ███████ ██
7 | ██ ██ ██ ██ ██ ██
8 | ██████ ██ ██ ██ ██ ██
9 |
10 |
11 | ██████ ██ ██ █████ ██████ ██████
12 | ██ ██ ██ ██ ██ ██ ██ ██ ██
13 | ██ ███ ██ ██ ███████ ██████ ██ ██
14 | ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
15 | ██████ ██████ ██ ██ ██ ██ ██████
16 |
17 | `;
18 |
19 | export const version = "V1";
20 | export const config: Config = {
21 | CONTACTS_STORAGE_KEY: "chatguard_contacts",
22 | ENCRYPT_PREFIX: `::CGM_${version}::`,
23 | HANDSHAKE_PREFIX: `::HSH_${version}::`,
24 | };
25 | export const selectors: Record
= {
26 | "web.bale.ai": {
27 | selector: {
28 | textField: (type) => (type === "mobile" ? "#main-message-input" : "#editable-message-text"),
29 | submitButton: (type) =>
30 | type === "mobile"
31 | ? "#chat_footer > :has(#main-message-input) :nth-child(5)"
32 | : "#chat_footer > :has(#editable-message-text) :nth-child(5)",
33 | header: "#toolbarWrapper",
34 | message: "[data-sid]",
35 | innerMessageText: "span",
36 | },
37 | events: {
38 | onSubmitClick: "click",
39 | onInput: "input",
40 | },
41 | path: "*",
42 | idProvider: "uid",
43 | },
44 | "web.telegram.org/k/": {
45 | selector: {
46 | textField: ".input-message-input[data-peer-id][contenteditable]",
47 | submitButton: ".btn-send-container button",
48 | header: ".chat .sidebar-header",
49 | message: ".chat [data-peer-id][data-mid]",
50 | innerMessageText: ".message",
51 | },
52 | events: {
53 | onSubmitClick: (type) => (type === "mobile" ? "mousedown" : "click"),
54 | onInput: "input",
55 | },
56 | path: "*",
57 | idProvider: "#",
58 | },
59 | "web.splus.ir": {
60 | selector: {
61 | textField: "#editable-message-text",
62 | header: "#MiddleColumn > div.messages-layout > div.MiddleHeader",
63 | message: "[data-message-id]",
64 | innerMessageText: ".contWrap",
65 | submitButton:
66 | "#MiddleColumn > div.messages-layout > div.Transition.slide > div > div.middle-column-footer > div > button",
67 | },
68 | events: {
69 | onSubmitClick: "click",
70 | onInput: "input",
71 | },
72 | path: "*",
73 | idProvider: "#",
74 | },
75 | "web.telegram.org/a/": {
76 | selector: {
77 | textField: "#editable-message-text",
78 | header: "#MiddleColumn > div.messages-layout > div.MiddleHeader",
79 | message: "[data-message-id]",
80 | innerMessageText: ".text-content",
81 | submitButton:
82 | "#MiddleColumn > div.messages-layout > div.Transition > div > div.middle-column-footer > div.Composer.shown.mounted > button",
83 | },
84 | events: {
85 | onSubmitClick: "click",
86 | onInput: "input",
87 | },
88 | path: "*",
89 | idProvider: "#",
90 | },
91 | "web.eitaa.com": {
92 | selector: {
93 | textField: ".chats-container .input-message-input",
94 | header: ".chats-container .sidebar-header",
95 | message: ".bubble[data-mid]",
96 | innerMessageText: ".message",
97 | submitButton: ".btn-send-container button",
98 | },
99 | events: {
100 | onSubmitClick: (type) => (type === "mobile" ? "mousedown" : "click"),
101 | onInput: "input",
102 | },
103 | path: "*",
104 | idProvider: "#",
105 | },
106 | "web.shad.ir": {
107 | selector: {
108 | textField: ".input-message-input [contenteditable]",
109 | header: ".chat-info-container",
110 | message: "[data-msg-id]",
111 | innerMessageText: ".message [rb-message-text] div",
112 | submitButton: ".btn-send-container button .c-ripple",
113 | },
114 | events: {
115 | onSubmitClick: "click",
116 | onInput: "keyup",
117 | },
118 | path: "*",
119 | idProvider: "#",
120 | },
121 | "web.rubika.ir": {
122 | selector: {
123 | textField: ".input-message-input [contenteditable]",
124 | header: ".chat-info-container",
125 | message: "[data-msg-id]",
126 | innerMessageText: ".message [rb-message-text] div",
127 | submitButton: ".btn-send-container button .c-ripple",
128 | },
129 | events: {
130 | onSubmitClick: "click",
131 | onInput: "keyup",
132 | },
133 | path: "*",
134 | idProvider: "#",
135 | },
136 | "web.igap.net": {
137 | selector: {
138 | textField: "#textarea_ref[contenteditable]",
139 | header: "header > div",
140 | message: "[data-message-id]",
141 | innerMessageText: "[data-message-id] > div [dir]",
142 | submitButton: "main > :last-child > :last-child",
143 | },
144 | events: {
145 | onSubmitClick: "click",
146 | onInput: "input",
147 | },
148 | path: "/app",
149 | idProvider: "q",
150 | },
151 | "twitter.com": {
152 | selector: {
153 | textField: "[contenteditable]",
154 | header: "[role=main] > div > div > div > :nth-child(2) > div > div > div > div > div > div > div",
155 | message: "[data-testid=messageEntry] > div > :nth-child(2) [role=presentation]",
156 | innerMessageText: "span",
157 | submitButton: "[role=complementary] > :nth-child(2) > [role=button]",
158 | },
159 | events: {
160 | onSubmitClick: "click",
161 | onInput: "input",
162 | },
163 | path: "/messages",
164 | idProvider: "/",
165 | },
166 | };
167 |
--------------------------------------------------------------------------------
/src/class/Cipher.ts:
--------------------------------------------------------------------------------
1 | import forge from "node-forge";
2 | import { config } from "src/config";
3 | import BrowserStorage from "src/utils/BrowserStorage";
4 | import LocalStorage from "src/utils/LocalStorage";
5 | import logger from "src/utils/logger";
6 |
7 | export class Cipher {
8 | public async createDRSAP(message: string, to: string) {
9 | if (message.trim() === "") return null;
10 |
11 | let store = await BrowserStorage.get();
12 | const secretKey = forge.random.getBytesSync(16);
13 | const hexSecret = forge.util.bytesToHex(secretKey);
14 |
15 | const ownPublicKey = forge.pki.publicKeyFromPem(store.user!.publicKey);
16 | const { publicKey: publicKeyPem } = LocalStorage.getMap(config.CONTACTS_STORAGE_KEY, to);
17 | if (!publicKeyPem) return null;
18 | const toPublicKey = forge.pki.publicKeyFromPem(publicKeyPem);
19 | const r1 = forge.util.bytesToHex(ownPublicKey.encrypt(secretKey));
20 | const r2 = forge.util.bytesToHex(toPublicKey.encrypt(secretKey));
21 | const encryptedMessage = await this.encryptAES(message, hexSecret);
22 | const template = config.ENCRYPT_PREFIX + r1 + r2 + encryptedMessage;
23 | return template;
24 | }
25 | public async resolveDRSAP(packet: string) {
26 | let store = await BrowserStorage.get();
27 | const packetArray = packet.split(config.ENCRYPT_PREFIX)[1].split("");
28 | const r1 = packetArray.splice(0, 128).join("");
29 | const r2 = packetArray.splice(0, 128).join("");
30 |
31 | const ownPrivateKey = forge.pki.privateKeyFromPem(store.user!.privateKey);
32 | let key;
33 | try {
34 | key = ownPrivateKey.decrypt(forge.util.hexToBytes(r1));
35 | } catch (error) {}
36 | if (!key) {
37 | try {
38 | key = ownPrivateKey.decrypt(forge.util.hexToBytes(r2));
39 | } catch (error) {}
40 | }
41 | if (!key) return null;
42 | const message = this.decryptAES(packetArray.join(""), forge.util.bytesToHex(key));
43 | return message;
44 | }
45 |
46 | public async createDRSAPHandshake(to: string) {
47 | const store = await BrowserStorage.get();
48 | const cleanedPublicKey = store.user!.publicKey.replace(/[\r\n]/g, "");
49 | const timestamp = new Date().getTime().toString();
50 | const packet =
51 | config.HANDSHAKE_PREFIX + btoa(store.user!.guardId) + cleanedPublicKey + timestamp.length + timestamp + btoa(to);
52 | return packet;
53 | }
54 | public async resolveDRSAPHandshake(packet: string, forId: string) {
55 | const store = await BrowserStorage.get();
56 | const handshakeArray = packet.split(config.HANDSHAKE_PREFIX)[1].split("");
57 | const guardId = atob(handshakeArray.splice(0, 48).join(""));
58 | const publicKey = handshakeArray.splice(0, 178).join("");
59 | const timestampLength = handshakeArray.splice(0, 2).join("");
60 | const timestamp = handshakeArray.splice(0, +timestampLength).join("");
61 | const toId = atob(handshakeArray.join(""));
62 |
63 | if (store.user?.guardId === guardId || forId === "") return;
64 | const oldContact = LocalStorage.getMap(config.CONTACTS_STORAGE_KEY, forId);
65 | if (+timestamp < +(oldContact.timestamp || 0)) return logger.debug(`Handshake ${toId} is old`);
66 | const allHandshakes = LocalStorage.get(config.CONTACTS_STORAGE_KEY);
67 | let isFound = false;
68 | for (let handshake in allHandshakes) {
69 | if (publicKey === allHandshakes[handshake].publicKey) isFound = true;
70 | }
71 | if (isFound) {
72 | logger.info(`Already have Handshake ${toId}`);
73 | if (!oldContact.publicKey) return;
74 | LocalStorage.setMap(config.CONTACTS_STORAGE_KEY, forId, {
75 | ...oldContact,
76 | });
77 | return;
78 | }
79 |
80 | LocalStorage.setMap(config.CONTACTS_STORAGE_KEY, forId, {
81 | publicKey,
82 | timestamp,
83 | enable: true,
84 | });
85 | logger.info(`New Handshake ${toId} registered`);
86 | return true;
87 | }
88 |
89 | public static validatePublicPem(pem: string) {
90 | try {
91 | forge.pki.publicKeyFromPem(pem);
92 | return true;
93 | } catch (error) {
94 | return false;
95 | }
96 | }
97 | public static validatePrivatePem(pem: string) {
98 | try {
99 | forge.pki.privateKeyFromPem(pem);
100 | return true;
101 | } catch (error) {
102 | return false;
103 | }
104 | }
105 | public async encryptAES(message: string, secretKey: string): Promise {
106 | const encoder = new TextEncoder();
107 | const data = encoder.encode(message);
108 |
109 | const cryptoKey = await window.crypto.subtle.importKey(
110 | "raw",
111 | encoder.encode(secretKey),
112 | { name: "AES-CBC" },
113 | false,
114 | ["encrypt"]
115 | );
116 |
117 | const iv = window.crypto.getRandomValues(new Uint8Array(16));
118 |
119 | const encryptedData = await window.crypto.subtle.encrypt({ name: "AES-CBC", iv: iv }, cryptoKey, data);
120 |
121 | const encryptedMessage = new Uint8Array(iv.length + encryptedData.byteLength);
122 | encryptedMessage.set(iv);
123 | encryptedMessage.set(new Uint8Array(encryptedData), iv.length);
124 |
125 | return Array.from(encryptedMessage)
126 | .map((byte) => ("0" + (byte & 0xff).toString(16)).slice(-2))
127 | .join("");
128 | }
129 |
130 | public async decryptAES(encryptedMessage: string, secretKey: string): Promise {
131 | const decoder = new TextDecoder();
132 | const encryptedData = new Uint8Array(encryptedMessage.match(/[\da-f]{2}/gi)!.map((hex) => parseInt(hex, 16)));
133 |
134 | const iv = encryptedData.slice(0, 16);
135 |
136 | const cryptoKey = await window.crypto.subtle.importKey(
137 | "raw",
138 | new TextEncoder().encode(secretKey),
139 | { name: "AES-CBC" },
140 | false,
141 | ["decrypt"]
142 | );
143 |
144 | const decryptedData = await window.crypto.subtle.decrypt(
145 | { name: "AES-CBC", iv: iv },
146 | cryptoKey,
147 | encryptedData.slice(16)
148 | );
149 |
150 | return decoder.decode(decryptedData);
151 | }
152 |
153 | static async generateKeyPair() {
154 | const keyPair = await forge.pki.rsa.generateKeyPair({ bits: 512, workers: 2 });
155 | const publicKey = forge.pki.publicKeyToPem(keyPair.publicKey);
156 | const privateKey = forge.pki.privateKeyToPem(keyPair.privateKey);
157 |
158 | return { privateKey, publicKey };
159 | }
160 | }
161 | export default Cipher;
162 |
--------------------------------------------------------------------------------
/src/components/modules/content/Actions.svelte:
--------------------------------------------------------------------------------
1 |
88 |
89 |
90 |
91 |
92 |
97 |
98 |
117 |
118 |
119 |
218 |
--------------------------------------------------------------------------------
/docs/public/images/appleStore.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2024 mostafa kheibary
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/docs/public/images/chromeStore.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/browser.d.ts:
--------------------------------------------------------------------------------
1 | // This Source Code Form is subject to the terms of the Mozilla Public
2 | // license, v. 2.0. If a copy of the MPL was not distributed with this
3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 |
5 | interface EvListener {
6 | addListener: (callback: T) => void;
7 | removeListener: (listener: T) => void;
8 | hasListener: (listener: T) => boolean;
9 | }
10 |
11 | type Listener = EvListener<(arg: T) => void>;
12 |
13 | declare namespace browser.alarms {
14 | type Alarm = {
15 | name: string;
16 | scheduledTime: number;
17 | periodInMinutes?: number;
18 | };
19 |
20 | type When = {
21 | when?: number;
22 | periodInMinutes?: number;
23 | };
24 | type DelayInMinutes = {
25 | delayInMinutes?: number;
26 | periodInMinutes?: number;
27 | };
28 | function create(name?: string, alarmInfo?: When | DelayInMinutes): void;
29 | function get(name?: string): Promise;
30 | function getAll(): Promise;
31 | function clear(name?: string): Promise;
32 | function clearAll(): Promise;
33 |
34 | const onAlarm: Listener;
35 | }
36 |
37 | declare namespace browser.bookmarks {
38 | type BookmarkTreeNodeUnmodifiable = "managed";
39 | type BookmarkTreeNodeType = "bookmark" | "folder" | "separator";
40 | type BookmarkTreeNode = {
41 | id: string;
42 | parentId?: string;
43 | index?: number;
44 | url?: string;
45 | title: string;
46 | dateAdded?: number;
47 | dateGroupModified?: number;
48 | unmodifiable?: BookmarkTreeNodeUnmodifiable;
49 | children?: BookmarkTreeNode[];
50 | type?: BookmarkTreeNodeType;
51 | };
52 |
53 | type CreateDetails = {
54 | parentId?: string;
55 | index?: number;
56 | title?: string;
57 | type?: BookmarkTreeNodeType;
58 | url?: string;
59 | };
60 |
61 | function create(bookmark: CreateDetails): Promise;
62 | function get(idOrIdList: string | string[]): Promise;
63 | function getChildren(id: string): Promise;
64 | function getRecent(numberOfItems: number): Promise;
65 | function getSubTree(id: string): Promise<[BookmarkTreeNode]>;
66 | function getTree(): Promise<[BookmarkTreeNode]>;
67 |
68 | type Destination =
69 | | {
70 | parentId: string;
71 | index?: number;
72 | }
73 | | {
74 | index: number;
75 | parentId?: string;
76 | };
77 | function move(id: string, destination: Destination): Promise;
78 | function remove(id: string): Promise;
79 | function removeTree(id: string): Promise;
80 | function search(
81 | query:
82 | | string
83 | | {
84 | query?: string;
85 | url?: string;
86 | title?: string;
87 | }
88 | ): Promise;
89 | function update(id: string, changes: { title: string; url: string }): Promise;
90 |
91 | const onCreated: EvListener<(id: string, bookmark: BookmarkTreeNode) => void>;
92 | const onRemoved: EvListener<
93 | (
94 | id: string,
95 | removeInfo: {
96 | parentId: string;
97 | index: number;
98 | node: BookmarkTreeNode;
99 | }
100 | ) => void
101 | >;
102 | const onChanged: EvListener<
103 | (
104 | id: string,
105 | changeInfo: {
106 | title: string;
107 | url?: string;
108 | }
109 | ) => void
110 | >;
111 | const onMoved: EvListener<
112 | (
113 | id: string,
114 | moveInfo: {
115 | parentId: string;
116 | index: number;
117 | oldParentId: string;
118 | oldIndex: number;
119 | }
120 | ) => void
121 | >;
122 | }
123 |
124 | declare namespace browser.browserAction {
125 | type ColorArray = [number, number, number, number];
126 | type ImageDataType = ImageData;
127 |
128 | function setTitle(details: { title: string | null; tabId?: number }): void;
129 | function getTitle(details: { tabId?: number }): Promise;
130 |
131 | type IconViaPath = {
132 | path: string | { [size: number]: string };
133 | tabId?: number;
134 | };
135 |
136 | type IconViaImageData = {
137 | imageData: ImageDataType | { [size: number]: ImageDataType };
138 | tabId?: number;
139 | };
140 |
141 | type IconReset = {
142 | imageData?: {} | null;
143 | path?: {} | null;
144 | tabId?: number;
145 | };
146 |
147 | function setIcon(details: IconViaPath | IconViaImageData | IconReset): Promise;
148 | function setPopup(details: { popup: string | null; tabId?: number }): void;
149 | function getPopup(details: { tabId?: number }): Promise;
150 | function openPopup(): Promise;
151 | function setBadgeText(details: { text: string | null; tabId?: number }): void;
152 | function getBadgeText(details: { tabId?: number }): Promise;
153 | function setBadgeBackgroundColor(details: { color: string | ColorArray | null; tabId?: number }): void;
154 | function getBadgeBackgroundColor(details: { tabId?: number }): Promise;
155 | function setBadgeTextColor(details: { color: string | ColorArray; tabId?: number }): void;
156 | function setBadgeTextColor(details: { color: string | ColorArray; windowId?: number }): void;
157 | function setBadgeTextColor(details: { color: null; tabId?: number }): void;
158 | function getBadgeTextColor(details: { tabId?: string }): Promise;
159 | function getBadgeTextColor(details: { windowId?: string }): Promise;
160 | function enable(tabId?: number): void;
161 | function disable(tabId?: number): void;
162 |
163 | const onClicked: Listener;
164 | }
165 |
166 | declare namespace browser.browsingData {
167 | type DataTypeSet = {
168 | cache?: boolean;
169 | cookies?: boolean;
170 | downloads?: boolean;
171 | fileSystems?: boolean;
172 | formData?: boolean;
173 | history?: boolean;
174 | indexedDB?: boolean;
175 | localStorage?: boolean;
176 | passwords?: boolean;
177 | pluginData?: boolean;
178 | serverBoundCertificates?: boolean;
179 | serviceWorkers?: boolean;
180 | };
181 |
182 | type DataRemovalOptions = {
183 | since?: number;
184 | originTypes?: { unprotectedWeb: boolean };
185 | };
186 |
187 | type ExtraDataRemovalOptions = {
188 | hostnames?: string[];
189 | };
190 |
191 | function remove(removalOptions: DataRemovalOptions, dataTypes: DataTypeSet): Promise;
192 | function removeCache(removalOptions?: DataRemovalOptions): Promise;
193 | function removeCookies(removalOptions: DataRemovalOptions & ExtraDataRemovalOptions): Promise;
194 | function removeLocalStorage(removalOptions: DataRemovalOptions & ExtraDataRemovalOptions): Promise;
195 | function removeDownloads(removalOptions: DataRemovalOptions): Promise;
196 | function removeFormData(removalOptions: DataRemovalOptions): Promise;
197 | function removeHistory(removalOptions: DataRemovalOptions): Promise;
198 | function removePasswords(removalOptions: DataRemovalOptions): Promise;
199 | function removePluginData(removalOptions: DataRemovalOptions): Promise;
200 | function settings(): Promise<{
201 | options: DataRemovalOptions;
202 | dataToRemove: DataTypeSet;
203 | dataRemovalPermitted: DataTypeSet;
204 | }>;
205 | }
206 |
207 | declare namespace browser.commands {
208 | type Command = {
209 | name?: string;
210 | description?: string;
211 | shortcut?: string;
212 | };
213 |
214 | function getAll(): Promise;
215 |
216 | const onCommand: Listener;
217 | }
218 |
219 | declare namespace browser.menus {
220 | type ContextType =
221 | | "all"
222 | | "audio"
223 | | "bookmarks"
224 | | "browser_action"
225 | | "editable"
226 | | "frame"
227 | | "image"
228 | // | "launcher" unsupported
229 | | "link"
230 | | "page"
231 | | "page_action"
232 | | "password"
233 | | "selection"
234 | | "tab"
235 | | "tools_menu"
236 | | "video";
237 |
238 | type ItemType = "normal" | "checkbox" | "radio" | "separator";
239 |
240 | type OnClickData = {
241 | bookmarkId?: string;
242 | checked?: boolean;
243 | editable: boolean;
244 | frameId?: number;
245 | frameUrl?: string;
246 | linkText?: string;
247 | linkUrl?: string;
248 | mediaType?: string;
249 | menuItemId: number | string;
250 | modifiers: string[];
251 | pageUrl?: string;
252 | parentMenuItemId?: number | string;
253 | selectionText?: string;
254 | srcUrl?: string;
255 | targetElementId?: number;
256 | wasChecked?: boolean;
257 | };
258 |
259 | const ACTION_MENU_TOP_LEVEL_LIMIT: number;
260 |
261 | function create(
262 | createProperties: {
263 | checked?: boolean;
264 | command?: "_execute_browser_action" | "_execute_page_action" | "_execute_sidebar_action";
265 | contexts?: ContextType[];
266 | documentUrlPatterns?: string[];
267 | enabled?: boolean;
268 | icons?: object;
269 | id?: string;
270 | onclick?: (info: OnClickData, tab: browser.tabs.Tab) => void;
271 | parentId?: number | string;
272 | targetUrlPatterns?: string[];
273 | title?: string;
274 | type?: ItemType;
275 | visible?: boolean;
276 | },
277 | callback?: () => void
278 | ): number | string;
279 |
280 | function getTargetElement(targetElementId: number): object | null;
281 |
282 | function refresh(): Promise;
283 |
284 | function remove(menuItemId: number | string): Promise;
285 |
286 | function removeAll(): Promise;
287 |
288 | function update(
289 | id: number | string,
290 | updateProperties: {
291 | checked?: boolean;
292 | command?: "_execute_browser_action" | "_execute_page_action" | "_execute_sidebar_action";
293 | contexts?: ContextType[];
294 | documentUrlPatterns?: string[];
295 | enabled?: boolean;
296 | onclick?: (info: OnClickData, tab: browser.tabs.Tab) => void;
297 | parentId?: number | string;
298 | targetUrlPatterns?: string[];
299 | title?: string;
300 | type?: ItemType;
301 | visible?: boolean;
302 | }
303 | ): Promise;
304 |
305 | const onClicked: EvListener<(info: OnClickData, tab: browser.tabs.Tab) => void>;
306 |
307 | const onHidden: EvListener<() => void>;
308 |
309 | const onShown: EvListener<(info: OnClickData, tab: browser.tabs.Tab) => void>;
310 | }
311 |
312 | declare namespace browser.contextualIdentities {
313 | type IdentityColor = "blue" | "turquoise" | "green" | "yellow" | "orange" | "red" | "pink" | "purple";
314 | type IdentityIcon = "fingerprint" | "briefcase" | "dollar" | "cart" | "circle";
315 |
316 | type ContextualIdentity = {
317 | cookieStoreId: string;
318 | color: IdentityColor;
319 | icon: IdentityIcon;
320 | name: string;
321 | };
322 |
323 | function create(details: { name: string; color: IdentityColor; icon: IdentityIcon }): Promise;
324 | function get(cookieStoreId: string): Promise;
325 | function query(details: { name?: string }): Promise;
326 | function update(
327 | cookieStoreId: string,
328 | details: {
329 | name: string;
330 | color: IdentityColor;
331 | icon: IdentityIcon;
332 | }
333 | ): Promise;
334 | function remove(cookieStoreId: string): Promise;
335 | }
336 |
337 | declare namespace browser.cookies {
338 | type Cookie = {
339 | name: string;
340 | value: string;
341 | domain: string;
342 | hostOnly: boolean;
343 | path: string;
344 | secure: boolean;
345 | httpOnly: boolean;
346 | session: boolean;
347 | firstPartyDomain?: string;
348 | sameSite: SameSiteStatus;
349 | expirationDate?: number;
350 | storeId: string;
351 | };
352 |
353 | type CookieStore = {
354 | id: string;
355 | incognito: boolean;
356 | tabIds: number[];
357 | };
358 |
359 | type SameSiteStatus = "no_restriction" | "lax" | "strict";
360 |
361 | type OnChangedCause = "evicted" | "expired" | "explicit" | "expired_overwrite" | "overwrite";
362 |
363 | function get(details: {
364 | url: string;
365 | name: string;
366 | storeId?: string;
367 | firstPartyDomain?: string;
368 | }): Promise;
369 | function getAll(details: {
370 | url?: string;
371 | name?: string;
372 | domain?: string;
373 | path?: string;
374 | secure?: boolean;
375 | session?: boolean;
376 | storeId?: string;
377 | firstPartyDomain?: string;
378 | }): Promise;
379 | function set(details: {
380 | domain?: string;
381 | expirationDate?: number;
382 | firstPartyDomain?: string;
383 | httpOnly?: boolean;
384 | name?: string;
385 | path?: string;
386 | sameSite?: SameSiteStatus;
387 | secure?: boolean;
388 | storeId?: string;
389 | url: string;
390 | value?: string;
391 | }): Promise;
392 | function remove(details: {
393 | url: string;
394 | name: string;
395 | storeId?: string;
396 | firstPartyDomain?: string;
397 | }): Promise;
398 | function getAllCookieStores(): Promise;
399 |
400 | const onChanged: Listener<{
401 | removed: boolean;
402 | cookie: Cookie;
403 | cause: OnChangedCause;
404 | }>;
405 | }
406 |
407 | declare namespace browser.contentScripts {
408 | type RegisteredContentScriptOptions = {
409 | allFrames?: boolean;
410 | css?: ({ file: string } | { code: string })[];
411 | excludeGlobs?: string[];
412 | excludeMatches?: string[];
413 | includeGlobs?: string[];
414 | js?: ({ file: string } | { code: string })[];
415 | matchAboutBlank?: boolean;
416 | matches: string[];
417 | runAt?: "document_start" | "document_end" | "document_idle";
418 | };
419 |
420 | type RegisteredContentScript = {
421 | unregister: () => void;
422 | };
423 |
424 | function register(contentScriptOptions: RegisteredContentScriptOptions): Promise;
425 | }
426 |
427 | declare namespace browser.devtools.inspectedWindow {
428 | const tabId: number;
429 |
430 | function eval(
431 | expression: string
432 | ): Promise<[any, { isException: boolean; value: string } | { isError: boolean; code: string }]>;
433 |
434 | function reload(reloadOptions?: { ignoreCache?: boolean; userAgent?: string; injectedScript?: string }): void;
435 | }
436 |
437 | declare namespace browser.devtools.network {
438 | const onNavigated: Listener;
439 | }
440 |
441 | declare namespace browser.devtools.panels {
442 | type ExtensionPanel = {
443 | onShown: Listener;
444 | onHidden: Listener;
445 | };
446 |
447 | function create(title: string, iconPath: string, pagePath: string): Promise;
448 | }
449 |
450 | declare namespace browser.downloads {
451 | type FilenameConflictAction = "uniquify" | "overwrite" | "prompt";
452 |
453 | type InterruptReason =
454 | | "FILE_FAILED"
455 | | "FILE_ACCESS_DENIED"
456 | | "FILE_NO_SPACE"
457 | | "FILE_NAME_TOO_LONG"
458 | | "FILE_TOO_LARGE"
459 | | "FILE_VIRUS_INFECTED"
460 | | "FILE_TRANSIENT_ERROR"
461 | | "FILE_BLOCKED"
462 | | "FILE_SECURITY_CHECK_FAILED"
463 | | "FILE_TOO_SHORT"
464 | | "NETWORK_FAILED"
465 | | "NETWORK_TIMEOUT"
466 | | "NETWORK_DISCONNECTED"
467 | | "NETWORK_SERVER_DOWN"
468 | | "NETWORK_INVALID_REQUEST"
469 | | "SERVER_FAILED"
470 | | "SERVER_NO_RANGE"
471 | | "SERVER_BAD_CONTENT"
472 | | "SERVER_UNAUTHORIZED"
473 | | "SERVER_CERT_PROBLEM"
474 | | "SERVER_FORBIDDEN"
475 | | "USER_CANCELED"
476 | | "USER_SHUTDOWN"
477 | | "CRASH";
478 |
479 | type DangerType = "file" | "url" | "content" | "uncommon" | "host" | "unwanted" | "safe" | "accepted";
480 |
481 | type State = "in_progress" | "interrupted" | "complete";
482 |
483 | type DownloadItem = {
484 | id: number;
485 | url: string;
486 | referrer: string;
487 | filename: string;
488 | incognito: boolean;
489 | danger: string;
490 | mime: string;
491 | startTime: string;
492 | endTime?: string;
493 | estimatedEndTime?: string;
494 | state: string;
495 | paused: boolean;
496 | canResume: boolean;
497 | error?: string;
498 | bytesReceived: number;
499 | totalBytes: number;
500 | fileSize: number;
501 | exists: boolean;
502 | byExtensionId?: string;
503 | byExtensionName?: string;
504 | };
505 |
506 | type Delta = {
507 | current?: T;
508 | previous?: T;
509 | };
510 |
511 | type StringDelta = Delta;
512 | type DoubleDelta = Delta;
513 | type BooleanDelta = Delta;
514 | type DownloadTime = Date | string | number;
515 |
516 | type DownloadQuery = {
517 | query?: string[];
518 | startedBefore?: DownloadTime;
519 | startedAfter?: DownloadTime;
520 | endedBefore?: DownloadTime;
521 | endedAfter?: DownloadTime;
522 | totalBytesGreater?: number;
523 | totalBytesLess?: number;
524 | filenameRegex?: string;
525 | urlRegex?: string;
526 | limit?: number;
527 | orderBy?: string;
528 | id?: number;
529 | url?: string;
530 | filename?: string;
531 | danger?: DangerType;
532 | mime?: string;
533 | startTime?: string;
534 | endTime?: string;
535 | state?: State;
536 | paused?: boolean;
537 | error?: InterruptReason;
538 | bytesReceived?: number;
539 | totalBytes?: number;
540 | fileSize?: number;
541 | exists?: boolean;
542 | };
543 |
544 | function download(options: {
545 | url: string;
546 | filename?: string;
547 | conflictAction?: string;
548 | saveAs?: boolean;
549 | method?: string;
550 | headers?: { [key: string]: string };
551 | body?: string;
552 | }): Promise;
553 | function search(query: DownloadQuery): Promise;
554 | function pause(downloadId: number): Promise;
555 | function resume(downloadId: number): Promise;
556 | function cancel(downloadId: number): Promise;
557 | // unsupported: function getFileIcon(downloadId: number, options?: { size?: number }):
558 | // Promise;
559 | function open(downloadId: number): Promise;
560 | function show(downloadId: number): Promise;
561 | function showDefaultFolder(): void;
562 | function erase(query: DownloadQuery): Promise;
563 | function removeFile(downloadId: number): Promise;
564 | // unsupported: function acceptDanger(downloadId: number): Promise;
565 | // unsupported: function drag(downloadId: number): Promise;
566 | // unsupported: function setShelfEnabled(enabled: boolean): void;
567 |
568 | const onCreated: Listener;
569 | const onErased: Listener;
570 | const onChanged: Listener<{
571 | id: number;
572 | url?: StringDelta;
573 | filename?: StringDelta;
574 | danger?: StringDelta;
575 | mime?: StringDelta;
576 | startTime?: StringDelta;
577 | endTime?: StringDelta;
578 | state?: StringDelta;
579 | canResume?: BooleanDelta;
580 | paused?: BooleanDelta;
581 | error?: StringDelta;
582 | totalBytes?: DoubleDelta;
583 | fileSize?: DoubleDelta;
584 | exists?: BooleanDelta;
585 | }>;
586 | }
587 |
588 | declare namespace browser.events {
589 | type UrlFilter = {
590 | hostContains?: string;
591 | hostEquals?: string;
592 | hostPrefix?: string;
593 | hostSuffix?: string;
594 | pathContains?: string;
595 | pathEquals?: string;
596 | pathPrefix?: string;
597 | pathSuffix?: string;
598 | queryContains?: string;
599 | queryEquals?: string;
600 | queryPrefix?: string;
601 | querySuffix?: string;
602 | urlContains?: string;
603 | urlEquals?: string;
604 | urlMatches?: string;
605 | originAndPathMatches?: string;
606 | urlPrefix?: string;
607 | urlSuffix?: string;
608 | schemes?: string[];
609 | ports?: Array;
610 | };
611 | }
612 |
613 | declare namespace browser.extension {
614 | type ViewType = "tab" | "notification" | "popup";
615 |
616 | const lastError: string | null;
617 | const inIncognitoContext: boolean;
618 |
619 | function getURL(path: string): string;
620 | function getViews(fetchProperties?: { type?: ViewType; windowId?: number }): Window[];
621 | function getBackgroundPage(): Window;
622 | function isAllowedIncognitoAccess(): Promise;
623 | function isAllowedFileSchemeAccess(): Promise;
624 | // unsupported: events as they are deprecated
625 | }
626 |
627 | declare namespace browser.extensionTypes {
628 | type ImageFormat = "jpeg" | "png";
629 | type ImageDetails = {
630 | format: ImageFormat;
631 | quality: number;
632 | };
633 | type RunAt = "document_start" | "document_end" | "document_idle";
634 | type InjectDetails = {
635 | allFrames?: boolean;
636 | code?: string;
637 | file?: string;
638 | frameId?: number;
639 | matchAboutBlank?: boolean;
640 | runAt?: RunAt;
641 | };
642 | type InjectDetailsCSS = InjectDetails & { cssOrigin?: "user" | "author" };
643 | }
644 |
645 | declare namespace browser.find {
646 | type FindOptions = {
647 | tabid: number;
648 | caseSensitive: boolean;
649 | entireWord: boolean;
650 | includeRangeData: boolean;
651 | includeRectData: boolean;
652 | };
653 |
654 | type FindResults = {
655 | count: number;
656 | rangeData?: RangeData[];
657 | rectData?: RectData[];
658 | };
659 |
660 | type RangeData = {
661 | framePos: number;
662 | startTextNodePos: number;
663 | endTextNodePos: number;
664 | startOffset: number;
665 | endOffset: number;
666 | text: string;
667 | };
668 |
669 | type RectData = {
670 | rectsAndTexts: RectsAndTexts;
671 | text: string;
672 | };
673 |
674 | type RectsAndTexts = {
675 | rectList: RectItem[];
676 | textList: string[];
677 | };
678 |
679 | type RectItem = {
680 | top: number;
681 | left: number;
682 | bottom: number;
683 | right: number;
684 | };
685 |
686 | function find(query: string, object?: FindOptions): Promise;
687 | function highlightResults(): void;
688 | function removeHighlighting(): void;
689 | }
690 |
691 | declare namespace browser.history {
692 | type TransitionType =
693 | | "link"
694 | | "typed"
695 | | "auto_bookmark"
696 | | "auto_subframe"
697 | | "manual_subframe"
698 | | "generated"
699 | | "auto_toplevel"
700 | | "form_submit"
701 | | "reload"
702 | | "keyword"
703 | | "keyword_generated";
704 |
705 | type HistoryItem = {
706 | id: string;
707 | url?: string;
708 | title?: string;
709 | lastVisitTime?: number;
710 | visitCount?: number;
711 | typedCount?: number;
712 | };
713 |
714 | type VisitItem = {
715 | id: string;
716 | visitId: string;
717 | visitTime?: number;
718 | refferingVisitId: string;
719 | transition: TransitionType;
720 | };
721 |
722 | function search(query: {
723 | text: string;
724 | startTime?: number | string | Date;
725 | endTime?: number | string | Date;
726 | maxResults?: number;
727 | }): Promise;
728 |
729 | function getVisits(details: { url: string }): Promise;
730 |
731 | function addUrl(details: {
732 | url: string;
733 | title?: string;
734 | transition?: TransitionType;
735 | visitTime?: number | string | Date;
736 | }): Promise;
737 |
738 | function deleteUrl(details: { url: string }): Promise;
739 |
740 | function deleteRange(range: { startTime: number | string | Date; endTime: number | string | Date }): Promise;
741 |
742 | function deleteAll(): Promise;
743 |
744 | const onVisited: Listener;
745 |
746 | // TODO: Ensure that urls is not `urls: [string]` instead
747 | const onVisitRemoved: Listener<{ allHistory: boolean; urls: string[] }>;
748 | }
749 |
750 | declare namespace browser.i18n {
751 | type LanguageCode = string;
752 |
753 | function getAcceptLanguages(): Promise;
754 |
755 | function getMessage(messageName: string, substitutions?: string | string[]): string;
756 |
757 | function getUILanguage(): LanguageCode;
758 |
759 | function detectLanguage(text: string): Promise<{
760 | isReliable: boolean;
761 | languages: { language: LanguageCode; percentage: number }[];
762 | }>;
763 | }
764 |
765 | declare namespace browser.identity {
766 | function getRedirectURL(): string;
767 | function launchWebAuthFlow(details: { url: string; interactive: boolean }): Promise;
768 | }
769 |
770 | declare namespace browser.idle {
771 | type IdleState = "active" | "idle" /* unsupported: | "locked" */;
772 |
773 | function queryState(detectionIntervalInSeconds: number): Promise;
774 | function setDetectionInterval(intervalInSeconds: number): void;
775 |
776 | const onStateChanged: Listener;
777 | }
778 |
779 | declare namespace browser.management {
780 | type ExtensionInfo = {
781 | description: string;
782 | // unsupported: disabledReason: string,
783 | enabled: boolean;
784 | homepageUrl: string;
785 | hostPermissions: string[];
786 | icons: { size: number; url: string }[];
787 | id: string;
788 | installType: "admin" | "development" | "normal" | "sideload" | "other";
789 | mayDisable: boolean;
790 | name: string;
791 | // unsupported: offlineEnabled: boolean,
792 | optionsUrl: string;
793 | permissions: string[];
794 | shortName: string;
795 | // unsupported: type: string,
796 | updateUrl: string;
797 | version: string;
798 | // unsupported: versionName: string,
799 | };
800 |
801 | function getSelf(): Promise;
802 | function uninstallSelf(options: { showConfirmDialog: boolean; dialogMessage: string }): Promise;
803 | }
804 |
805 | declare namespace browser.notifications {
806 | type TemplateType = "basic" /* | "image" | "list" | "progress" */;
807 |
808 | type NotificationOptions = {
809 | type: TemplateType;
810 | message: string;
811 | title: string;
812 | iconUrl?: string;
813 | };
814 |
815 | function create(id: string | null, options: NotificationOptions): Promise;
816 | function create(options: NotificationOptions): Promise;
817 |
818 | function clear(id: string): Promise;
819 |
820 | function getAll(): Promise<{ [key: string]: NotificationOptions }>;
821 |
822 | const onClosed: Listener;
823 |
824 | const onClicked: Listener;
825 | }
826 |
827 | declare namespace browser.omnibox {
828 | type OnInputEnteredDisposition = "currentTab" | "newForegroundTab" | "newBackgroundTab";
829 | type SuggestResult = {
830 | content: string;
831 | description: string;
832 | };
833 |
834 | function setDefaultSuggestion(suggestion: { description: string }): void;
835 |
836 | const onInputStarted: Listener;
837 | const onInputChanged: EvListener<(text: string, suggest: (arg: SuggestResult[]) => void) => void>;
838 | const onInputEntered: EvListener<(text: string, disposition: OnInputEnteredDisposition) => void>;
839 | const onInputCancelled: Listener;
840 | }
841 |
842 | declare namespace browser.pageAction {
843 | type ImageDataType = ImageData;
844 |
845 | function show(tabId: number): void;
846 |
847 | function hide(tabId: number): void;
848 |
849 | function setTitle(details: { tabId: number; title: string }): void;
850 |
851 | function getTitle(details: { tabId: number }): Promise;
852 |
853 | function setIcon(details: { tabId: number; path?: string | object; imageData?: ImageDataType }): Promise;
854 |
855 | function setPopup(details: { tabId: number; popup: string }): void;
856 |
857 | function getPopup(details: { tabId: number }): Promise;
858 |
859 | const onClicked: Listener;
860 | }
861 |
862 | declare namespace browser.permissions {
863 | type Permission =
864 | | "activeTab"
865 | | "alarms"
866 | | "background"
867 | | "bookmarks"
868 | | "browsingData"
869 | | "browserSettings"
870 | | "clipboardRead"
871 | | "clipboardWrite"
872 | | "contextMenus"
873 | | "contextualIdentities"
874 | | "cookies"
875 | | "downloads"
876 | | "downloads.open"
877 | | "find"
878 | | "geolocation"
879 | | "history"
880 | | "identity"
881 | | "idle"
882 | | "management"
883 | | "menus"
884 | | "nativeMessaging"
885 | | "notifications"
886 | | "pkcs11"
887 | | "privacy"
888 | | "proxy"
889 | | "sessions"
890 | | "storage"
891 | | "tabs"
892 | | "theme"
893 | | "topSites"
894 | | "unlimitedStorage"
895 | | "webNavigation"
896 | | "webRequest"
897 | | "webRequestBlocking";
898 |
899 | type Permissions = {
900 | origins?: string[];
901 | permissions?: Permission[];
902 | };
903 |
904 | function contains(permissions: Permissions): Promise;
905 |
906 | function getAll(): Promise;
907 |
908 | function remove(permissions: Permissions): Promise;
909 |
910 | function request(permissions: Permissions): Promise;
911 |
912 | // Not yet support in Edge and Firefox:
913 | // const onAdded: Listener;
914 | // const onRemoved: Listener;
915 | }
916 |
917 | declare namespace browser.runtime {
918 | const lastError: string | null;
919 | const id: string;
920 |
921 | type Port = {
922 | name: string;
923 | disconnect(): void;
924 | error: object;
925 | onDisconnect: Listener;
926 | onMessage: Listener;
927 | postMessage: (message: T) => void;
928 | sender?: runtime.MessageSender;
929 | };
930 |
931 | type MessageSender = {
932 | tab?: browser.tabs.Tab;
933 | frameId?: number;
934 | id?: string;
935 | url?: string;
936 | tlsChannelId?: string;
937 | };
938 |
939 | type PlatformOs = "mac" | "win" | "android" | "cros" | "linux" | "openbsd";
940 | type PlatformArch = "arm" | "x86-32" | "x86-64";
941 | type PlatformNaclArch = "arm" | "x86-32" | "x86-64";
942 |
943 | type PlatformInfo = {
944 | os: PlatformOs;
945 | arch: PlatformArch;
946 | };
947 |
948 | // type RequestUpdateCheckStatus = "throttled" | "no_update" | "update_available";
949 | type OnInstalledReason = "install" | "update" | "chrome_update" | "shared_module_update";
950 | type OnRestartRequiredReason = "app_update" | "os_update" | "periodic";
951 |
952 | type FirefoxSpecificProperties = {
953 | id?: string;
954 | strict_min_version?: string;
955 | strict_max_version?: string;
956 | update_url?: string;
957 | };
958 |
959 | type IconPath = { [urlName: string]: string } | string;
960 |
961 | type Manifest = {
962 | // Required
963 | manifest_version: 2;
964 | name: string;
965 | version: string;
966 | /** Required in Microsoft Edge */
967 | author?: string;
968 |
969 | // Optional
970 |
971 | // ManifestBase
972 | description?: string;
973 | homepage_url?: string;
974 | short_name?: string;
975 |
976 | // WebExtensionManifest
977 | background?: {
978 | page: string;
979 | scripts: string[];
980 | persistent?: boolean;
981 | };
982 | content_scripts?: {
983 | matches: string[];
984 | exclude_matches?: string[];
985 | include_globs?: string[];
986 | exclude_globs?: string[];
987 | css?: string[];
988 | js?: string[];
989 | all_frames?: boolean;
990 | match_about_blank?: boolean;
991 | run_at?: "document_start" | "document_end" | "document_idle";
992 | }[];
993 | content_security_policy?: string;
994 | developer?: {
995 | name?: string;
996 | url?: string;
997 | };
998 | icons?: {
999 | [imgSize: string]: string;
1000 | };
1001 | incognito?: "spanning" | "split" | "not_allowed";
1002 | optional_permissions?: browser.permissions.Permission[];
1003 | options_ui?: {
1004 | page: string;
1005 | browser_style?: boolean;
1006 | chrome_style?: boolean;
1007 | open_in_tab?: boolean;
1008 | };
1009 | permissions?: browser.permissions.Permission[];
1010 | web_accessible_resources?: string[];
1011 |
1012 | // WebExtensionLangpackManifest
1013 | languages: {
1014 | [langCode: string]: {
1015 | chrome_resources: {
1016 | [resName: string]: string | { [urlName: string]: string };
1017 | };
1018 | version: string;
1019 | };
1020 | };
1021 | langpack_id?: string;
1022 | sources?: {
1023 | [srcName: string]: {
1024 | base_path: string;
1025 | paths?: string[];
1026 | };
1027 | };
1028 |
1029 | // Extracted from components
1030 | browser_action?: {
1031 | default_title?: string;
1032 | default_icon?: IconPath;
1033 | theme_icons?: {
1034 | light: string;
1035 | dark: string;
1036 | size: number;
1037 | }[];
1038 | default_popup?: string;
1039 | browser_style?: boolean;
1040 | default_area?: "navbar" | "menupanel" | "tabstrip" | "personaltoolbar";
1041 | };
1042 | commands?: {
1043 | [keyName: string]: {
1044 | suggested_key?: {
1045 | default?: string;
1046 | mac?: string;
1047 | linux?: string;
1048 | windows?: string;
1049 | chromeos?: string;
1050 | android?: string;
1051 | ios?: string;
1052 | };
1053 | description?: string;
1054 | };
1055 | };
1056 | default_locale?: browser.i18n.LanguageCode;
1057 | devtools_page?: string;
1058 | omnibox?: {
1059 | keyword: string;
1060 | };
1061 | page_action?: {
1062 | default_title?: string;
1063 | default_icon?: IconPath;
1064 | default_popup?: string;
1065 | browser_style?: boolean;
1066 | show_matches?: string[];
1067 | hide_matches?: string[];
1068 | };
1069 | sidebar_action?: {
1070 | default_panel: string;
1071 | default_title?: string;
1072 | default_icon?: IconPath;
1073 | browser_style?: boolean;
1074 | };
1075 |
1076 | // Firefox specific
1077 | applications?: {
1078 | gecko?: FirefoxSpecificProperties;
1079 | };
1080 | browser_specific_settings?: {
1081 | gecko?: FirefoxSpecificProperties;
1082 | };
1083 | experiment_apis?: any;
1084 | protocol_handlers?: {
1085 | name: string;
1086 | protocol: string;
1087 | uriTemplate: string;
1088 | };
1089 |
1090 | // Opera specific
1091 | minimum_opera_version?: string;
1092 |
1093 | // Chrome specific
1094 | action?: any;
1095 | automation?: any;
1096 | background_page?: any;
1097 | chrome_settings_overrides?: {
1098 | homepage?: string;
1099 | search_provider?: {
1100 | name: string;
1101 | search_url: string;
1102 | keyword?: string;
1103 | favicon_url?: string;
1104 | suggest_url?: string;
1105 | instant_url?: string;
1106 | is_default?: string;
1107 | image_url?: string;
1108 | search_url_post_params?: string;
1109 | instant_url_post_params?: string;
1110 | image_url_post_params?: string;
1111 | alternate_urls?: string[];
1112 | prepopulated_id?: number;
1113 | };
1114 | };
1115 | chrome_ui_overrides?: {
1116 | bookmarks_ui?: {
1117 | remove_bookmark_shortcut?: true;
1118 | remove_button?: true;
1119 | };
1120 | };
1121 | chrome_url_overrides?: {
1122 | newtab?: string;
1123 | bookmarks?: string;
1124 | history?: string;
1125 | };
1126 | content_capabilities?: any;
1127 | converted_from_user_script?: any;
1128 | current_locale?: any;
1129 | declarative_net_request?: any;
1130 | event_rules?: any[];
1131 | export?: {
1132 | whitelist?: string[];
1133 | };
1134 | externally_connectable?: {
1135 | ids?: string[];
1136 | matches?: string[];
1137 | accepts_tls_channel_id?: boolean;
1138 | };
1139 | file_browser_handlers?: {
1140 | id: string;
1141 | default_title: string;
1142 | file_filters: string[];
1143 | }[];
1144 | file_system_provider_capabilities?: {
1145 | source: "file" | "device" | "network";
1146 | configurable?: boolean;
1147 | multiple_mounts?: boolean;
1148 | watchable?: boolean;
1149 | };
1150 | import?: {
1151 | id: string;
1152 | minimum_version?: string;
1153 | }[];
1154 | input_components?: any;
1155 | key?: string;
1156 | minimum_chrome_version?: string;
1157 | nacl_modules?: {
1158 | path: string;
1159 | mime_type: string;
1160 | }[];
1161 | oauth2?: any;
1162 | offline_enabled?: boolean;
1163 | options_page?: string;
1164 | platforms?: any;
1165 | requirements?: any;
1166 | sandbox?: {
1167 | pages: string[];
1168 | content_security_policy?: string;
1169 | }[];
1170 | signature?: any;
1171 | spellcheck?: any;
1172 | storage?: {
1173 | managed_schema: string;
1174 | };
1175 | system_indicator?: any;
1176 | tts_engine?: {
1177 | voice: {
1178 | voice_name: string;
1179 | lang?: string;
1180 | gender?: "male" | "female";
1181 | event_types: ("start" | "word" | "sentence" | "marker" | "end" | "error")[];
1182 | }[];
1183 | };
1184 | update_url?: string;
1185 | version_name?: string;
1186 | };
1187 |
1188 | function getBackgroundPage(): Promise;
1189 | function openOptionsPage(): Promise;
1190 | function getManifest(): Manifest;
1191 |
1192 | function getURL(path: string): string;
1193 | function setUninstallURL(url: string): Promise;
1194 | function reload(): void;
1195 | // Will not exist: https://bugzilla.mozilla.org/show_bug.cgi?id=1314922
1196 | // function RequestUpdateCheck(): Promise;
1197 | function connect(connectInfo?: { name?: string; includeTlsChannelId?: boolean }): Port;
1198 | function connect(extensionId?: string, connectInfo?: { name?: string; includeTlsChannelId?: boolean }): Port;
1199 | function connectNative(application: string): Port;
1200 |
1201 | function sendMessage(message: T): Promise;
1202 | function sendMessage(
1203 | message: T,
1204 | options: { includeTlsChannelId?: boolean; toProxyScript?: boolean }
1205 | ): Promise;
1206 | function sendMessage(extensionId: string, message: T): Promise;
1207 | function sendMessage(
1208 | extensionId: string,
1209 | message: T,
1210 | options?: { includeTlsChannelId?: boolean; toProxyScript?: boolean }
1211 | ): Promise;
1212 |
1213 | function sendNativeMessage(application: string, message: object): Promise;
1214 | function getPlatformInfo(): Promise;
1215 | function getBrowserInfo(): Promise<{
1216 | name: string;
1217 | vendor: string;
1218 | version: string;
1219 | buildID: string;
1220 | }>;
1221 | // Unsupported: https://bugzilla.mozilla.org/show_bug.cgi?id=1339407
1222 | // function getPackageDirectoryEntry(): Promise;
1223 |
1224 | const onStartup: Listener;
1225 | const onInstalled: Listener<{
1226 | reason: OnInstalledReason;
1227 | previousVersion?: string;
1228 | id?: string;
1229 | }>;
1230 | // Unsupported
1231 | // const onSuspend: Listener;
1232 | // const onSuspendCanceled: Listener;
1233 | // const onBrowserUpdateAvailable: Listener;
1234 | // const onRestartRequired: Listener;
1235 | const onUpdateAvailable: Listener<{ version: string }>;
1236 | const onConnect: Listener;
1237 |
1238 | const onConnectExternal: Listener;
1239 |
1240 | type onMessagePromise = (
1241 | message: object,
1242 | sender: MessageSender,
1243 | sendResponse: (response: object) => boolean
1244 | ) => Promise;
1245 |
1246 | type onMessageBool = (
1247 | message: object,
1248 | sender: MessageSender,
1249 | sendResponse: (response: object) => Promise
1250 | ) => boolean;
1251 |
1252 | type onMessageVoid = (
1253 | message: object,
1254 | sender: MessageSender,
1255 | sendResponse: (response: object) => Promise
1256 | ) => void;
1257 |
1258 | type onMessageEvent = onMessagePromise | onMessageBool | onMessageVoid;
1259 | const onMessage: EvListener;
1260 |
1261 | const onMessageExternal: EvListener;
1262 | }
1263 |
1264 | declare namespace browser.sessions {
1265 | type Filter = { maxResults?: number };
1266 |
1267 | type Session = {
1268 | lastModified: number;
1269 | tab: browser.tabs.Tab;
1270 | window: browser.windows.Window;
1271 | };
1272 |
1273 | const MAX_SESSION_RESULTS: number;
1274 |
1275 | function getRecentlyClosed(filter?: Filter): Promise;
1276 |
1277 | function restore(sessionId: string): Promise;
1278 |
1279 | function setTabValue(tabId: number, key: string, value: string | object): Promise;
1280 |
1281 | function getTabValue(tabId: number, key: string): Promise;
1282 |
1283 | function removeTabValue(tabId: number, key: string): Promise;
1284 |
1285 | function setWindowValue(windowId: number, key: string, value: string | object): Promise;
1286 |
1287 | function getWindowValue(windowId: number, key: string): Promise;
1288 |
1289 | function removeWindowValue(windowId: number, key: string): Promise;
1290 |
1291 | const onChanged: EvListener<() => void>;
1292 | }
1293 |
1294 | declare namespace browser.sidebarAction {
1295 | type ImageDataType = ImageData;
1296 |
1297 | function setPanel(details: { panel: string; tabId?: number }): void;
1298 |
1299 | function getPanel(details: { tabId?: number }): Promise;
1300 |
1301 | function setTitle(details: { title: string; tabId?: number }): void;
1302 |
1303 | function getTitle(details: { tabId?: number }): Promise;
1304 |
1305 | type IconViaPath = {
1306 | path: string | { [index: number]: string };
1307 | tabId?: number;
1308 | };
1309 |
1310 | type IconViaImageData = {
1311 | imageData: ImageDataType | { [index: number]: ImageDataType };
1312 | tabId?: number;
1313 | };
1314 |
1315 | function setIcon(details: IconViaPath | IconViaImageData): Promise;
1316 |
1317 | function open(): Promise;
1318 |
1319 | function close(): Promise;
1320 | }
1321 |
1322 | declare namespace browser.storage {
1323 | // Non-firefox implementations don't accept all these types
1324 | type StorageValue =
1325 | | string
1326 | | number
1327 | | boolean
1328 | | null
1329 | | undefined
1330 | | RegExp
1331 | | ArrayBuffer
1332 | | Uint8ClampedArray
1333 | | Uint8Array
1334 | | Uint16Array
1335 | | Uint32Array
1336 | | Int8Array
1337 | | Int16Array
1338 | | Int32Array
1339 | | Float32Array
1340 | | Float64Array
1341 | | DataView
1342 | | StorageArray
1343 | | StorageMap
1344 | | StorageSet
1345 | | StorageObject;
1346 |
1347 | // The Index signature makes casting to/from classes or interfaces a pain.
1348 | // Custom types are OK.
1349 | interface StorageObject {
1350 | [key: string]: StorageValue;
1351 | }
1352 | // These have to be interfaces rather than types to avoid a circular
1353 | // definition of StorageValue
1354 | interface StorageArray extends Array {}
1355 | interface StorageMap extends Map {}
1356 | interface StorageSet extends Set {}
1357 |
1358 | interface Get {
1359 | (keys?: string | string[] | null): Promise;
1360 | /* (keys: T): Promise<{[K in keyof T]: T[K]}>; */
1361 | (keys: T): Promise;
1362 | }
1363 |
1364 | type StorageArea = {
1365 | get: Get;
1366 | // unsupported: getBytesInUse: (keys: string|string[]|null) => Promise,
1367 | set: (keys: StorageObject) => Promise;
1368 | remove: (keys: string | string[]) => Promise;
1369 | clear: () => Promise;
1370 | };
1371 |
1372 | type StorageChange = {
1373 | oldValue?: any;
1374 | newValue?: any;
1375 | };
1376 |
1377 | const sync: StorageArea;
1378 | const local: StorageArea;
1379 | // unsupported: const managed: StorageArea;
1380 |
1381 | type ChangeDict = { [field: string]: StorageChange };
1382 | type StorageName = "sync" | "local" /* |"managed" */;
1383 |
1384 | const onChanged: EvListener<(changes: ChangeDict, areaName: StorageName) => void>;
1385 | }
1386 |
1387 | declare namespace browser.tabs {
1388 | type MutedInfoReason = "capture" | "extension" | "user";
1389 | type MutedInfo = {
1390 | muted: boolean;
1391 | extensionId?: string;
1392 | reason: MutedInfoReason;
1393 | };
1394 | // TODO: Specify PageSettings properly.
1395 | type PageSettings = object;
1396 | type Tab = {
1397 | active: boolean;
1398 | audible?: boolean;
1399 | autoDiscardable?: boolean;
1400 | cookieStoreId?: string;
1401 | discarded?: boolean;
1402 | favIconUrl?: string;
1403 | height?: number;
1404 | hidden: boolean;
1405 | highlighted: boolean;
1406 | id?: number;
1407 | incognito: boolean;
1408 | index: number;
1409 | isArticle: boolean;
1410 | isInReaderMode: boolean;
1411 | lastAccessed: number;
1412 | mutedInfo?: MutedInfo;
1413 | openerTabId?: number;
1414 | pinned: boolean;
1415 | selected: boolean;
1416 | sessionId?: string;
1417 | status?: string;
1418 | title?: string;
1419 | url?: string;
1420 | width?: number;
1421 | windowId: number;
1422 | };
1423 |
1424 | type TabStatus = "loading" | "complete";
1425 | type WindowType = "normal" | "popup" | "panel" | "devtools";
1426 | type ZoomSettingsMode = "automatic" | "disabled" | "manual";
1427 | type ZoomSettingsScope = "per-origin" | "per-tab";
1428 | type ZoomSettings = {
1429 | defaultZoomFactor?: number;
1430 | mode?: ZoomSettingsMode;
1431 | scope?: ZoomSettingsScope;
1432 | };
1433 |
1434 | const TAB_ID_NONE: number;
1435 |
1436 | function connect(tabId: number, connectInfo?: { name?: string; frameId?: number }): browser.runtime.Port;
1437 | function create(createProperties: {
1438 | active?: boolean;
1439 | cookieStoreId?: string;
1440 | index?: number;
1441 | openerTabId?: number;
1442 | pinned?: boolean;
1443 | // deprecated: selected: boolean,
1444 | url?: string;
1445 | windowId?: number;
1446 | }): Promise;
1447 | function captureTab(tabId?: number, options?: browser.extensionTypes.ImageDetails): Promise;
1448 | function captureVisibleTab(windowId?: number, options?: browser.extensionTypes.ImageDetails): Promise;
1449 | function detectLanguage(tabId?: number): Promise;
1450 | function duplicate(tabId: number): Promise;
1451 | function executeScript(tabId: number | undefined, details: browser.extensionTypes.InjectDetails): Promise;
1452 | function get(tabId: number): Promise;
1453 | // deprecated: function getAllInWindow(): x;
1454 | function getCurrent(): Promise;
1455 | // deprecated: function getSelected(windowId?: number): Promise;
1456 | function getZoom(tabId?: number): Promise;
1457 | function getZoomSettings(tabId?: number): Promise;
1458 | function hide(tabIds: number | number[]): Promise;
1459 | // unsupported: function highlight(highlightInfo: {
1460 | // windowId?: number,
1461 | // tabs: number[]|number,
1462 | // }): Promise;
1463 | function insertCSS(tabId: number | undefined, details: browser.extensionTypes.InjectDetailsCSS): Promise;
1464 | function removeCSS(tabId: number | undefined, details: browser.extensionTypes.InjectDetails): Promise;
1465 | function move(
1466 | tabIds: number | number[],
1467 | moveProperties: {
1468 | windowId?: number;
1469 | index: number;
1470 | }
1471 | ): Promise;
1472 | function print(): Promise;
1473 | function printPreview(): Promise;
1474 | function query(queryInfo: {
1475 | active?: boolean;
1476 | audible?: boolean;
1477 | // unsupported: autoDiscardable?: boolean,
1478 | cookieStoreId?: string;
1479 | currentWindow?: boolean;
1480 | discarded?: boolean;
1481 | hidden?: boolean;
1482 | highlighted?: boolean;
1483 | index?: number;
1484 | muted?: boolean;
1485 | lastFocusedWindow?: boolean;
1486 | pinned?: boolean;
1487 | status?: TabStatus;
1488 | title?: string;
1489 | url?: string | string[];
1490 | windowId?: number;
1491 | windowType?: WindowType;
1492 | }): Promise;
1493 | function reload(tabId?: number, reloadProperties?: { bypassCache?: boolean }): Promise;
1494 | function remove(tabIds: number | number[]): Promise;
1495 | function saveAsPDF(
1496 | pageSettings: PageSettings
1497 | ): Promise<"saved" | "replaced" | "canceled" | "not_saved" | "not_replaced">;
1498 | function sendMessage(
1499 | tabId: number,
1500 | message: T,
1501 | options?: { frameId?: number }
1502 | ): Promise;
1503 | // deprecated: function sendRequest(): x;
1504 | function setZoom(tabId: number | undefined, zoomFactor: number): Promise;
1505 | function setZoomSettings(tabId: number | undefined, zoomSettings: ZoomSettings): Promise;
1506 | function show(tabIds: number | number[]): Promise;
1507 | function toggleReaderMode(tabId?: number): Promise;
1508 | function update(
1509 | tabId: number | undefined,
1510 | updateProperties: {
1511 | active?: boolean;
1512 | // unsupported: autoDiscardable?: boolean,
1513 | // unsupported: highlighted?: boolean,
1514 | // unsupported: hidden?: boolean;
1515 | loadReplace?: boolean;
1516 | muted?: boolean;
1517 | openerTabId?: number;
1518 | pinned?: boolean;
1519 | // deprecated: selected?: boolean,
1520 | url?: string;
1521 | }
1522 | ): Promise;
1523 |
1524 | const onActivated: Listener<{ tabId: number; windowId: number }>;
1525 | const onAttached: EvListener<
1526 | (
1527 | tabId: number,
1528 | attachInfo: {
1529 | newWindowId: number;
1530 | newPosition: number;
1531 | }
1532 | ) => void
1533 | >;
1534 | const onCreated: Listener;
1535 | const onDetached: EvListener<
1536 | (
1537 | tabId: number,
1538 | detachInfo: {
1539 | oldWindowId: number;
1540 | oldPosition: number;
1541 | }
1542 | ) => void
1543 | >;
1544 | const onHighlighted: Listener<{ windowId: number; tabIds: number[] }>;
1545 | const onMoved: EvListener<
1546 | (
1547 | tabId: number,
1548 | moveInfo: {
1549 | windowId: number;
1550 | fromIndex: number;
1551 | toIndex: number;
1552 | }
1553 | ) => void
1554 | >;
1555 | const onRemoved: EvListener<
1556 | (
1557 | tabId: number,
1558 | removeInfo: {
1559 | windowId: number;
1560 | isWindowClosing: boolean;
1561 | }
1562 | ) => void
1563 | >;
1564 | const onReplaced: EvListener<(addedTabId: number, removedTabId: number) => void>;
1565 | const onUpdated: EvListener<
1566 | (
1567 | tabId: number,
1568 | changeInfo: {
1569 | audible?: boolean;
1570 | discarded?: boolean;
1571 | favIconUrl?: string;
1572 | mutedInfo?: MutedInfo;
1573 | pinned?: boolean;
1574 | status?: string;
1575 | title?: string;
1576 | url?: string;
1577 | },
1578 | tab: Tab
1579 | ) => void
1580 | >;
1581 | const onZoomChanged: Listener<{
1582 | tabId: number;
1583 | oldZoomFactor: number;
1584 | newZoomFactor: number;
1585 | zoomSettings: ZoomSettings;
1586 | }>;
1587 | }
1588 |
1589 | declare namespace browser.topSites {
1590 | type MostVisitedURL = {
1591 | title: string;
1592 | url: string;
1593 | };
1594 | function get(): Promise;
1595 | }
1596 |
1597 | declare namespace browser.webNavigation {
1598 | type TransitionType = "link" | "auto_subframe" | "form_submit" | "reload";
1599 | // unsupported: | "typed" | "auto_bookmark" | "manual_subframe"
1600 | // | "generated" | "start_page" | "keyword"
1601 | // | "keyword_generated";
1602 |
1603 | type TransitionQualifier = "client_redirect" | "server_redirect" | "forward_back";
1604 | // unsupported: "from_address_bar";
1605 |
1606 | function getFrame(details: {
1607 | tabId: number;
1608 | processId: number;
1609 | frameId: number;
1610 | }): Promise<{ errorOccured: boolean; url: string; parentFrameId: number }>;
1611 |
1612 | function getAllFrames(details: { tabId: number }): Promise<
1613 | {
1614 | errorOccured: boolean;
1615 | processId: number;
1616 | frameId: number;
1617 | parentFrameId: number;
1618 | url: string;
1619 | }[]
1620 | >;
1621 |
1622 | interface NavListener {
1623 | addListener: (
1624 | callback: (arg: T) => void,
1625 | filter?: {
1626 | url: browser.events.UrlFilter[];
1627 | }
1628 | ) => void;
1629 | removeListener: (callback: (arg: T) => void) => void;
1630 | hasListener: (callback: (arg: T) => void) => boolean;
1631 | }
1632 |
1633 | type DefaultNavListener = NavListener<{
1634 | tabId: number;
1635 | url: string;
1636 | processId: number;
1637 | frameId: number;
1638 | timeStamp: number;
1639 | }>;
1640 |
1641 | type TransitionNavListener = NavListener<{
1642 | tabId: number;
1643 | url: string;
1644 | processId: number;
1645 | frameId: number;
1646 | timeStamp: number;
1647 | transitionType: TransitionType;
1648 | transitionQualifiers: TransitionQualifier[];
1649 | }>;
1650 |
1651 | const onBeforeNavigate: NavListener<{
1652 | tabId: number;
1653 | url: string;
1654 | processId: number;
1655 | frameId: number;
1656 | parentFrameId: number;
1657 | timeStamp: number;
1658 | }>;
1659 |
1660 | const onCommitted: TransitionNavListener;
1661 |
1662 | const onCreatedNavigationTarget: NavListener<{
1663 | sourceFrameId: number;
1664 | // Unsupported: sourceProcessId: number,
1665 | sourceTabId: number;
1666 | tabId: number;
1667 | timeStamp: number;
1668 | url: string;
1669 | windowId: number;
1670 | }>;
1671 |
1672 | const onDOMContentLoaded: DefaultNavListener;
1673 |
1674 | const onCompleted: DefaultNavListener;
1675 |
1676 | const onErrorOccurred: DefaultNavListener; // error field unsupported
1677 |
1678 | const onReferenceFragmentUpdated: TransitionNavListener;
1679 |
1680 | const onHistoryStateUpdated: TransitionNavListener;
1681 | }
1682 |
1683 | declare namespace browser.webRequest {
1684 | type ResourceType =
1685 | | "main_frame"
1686 | | "sub_frame"
1687 | | "stylesheet"
1688 | | "script"
1689 | | "image"
1690 | | "object"
1691 | | "xmlhttprequest"
1692 | | "xbl"
1693 | | "xslt"
1694 | | "ping"
1695 | | "beacon"
1696 | | "xml_dtd"
1697 | | "font"
1698 | | "media"
1699 | | "websocket"
1700 | | "csp_report"
1701 | | "imageset"
1702 | | "web_manifest"
1703 | | "other";
1704 |
1705 | type RequestFilter = {
1706 | urls: string[];
1707 | types?: ResourceType[];
1708 | tabId?: number;
1709 | windowId?: number;
1710 | };
1711 |
1712 | type StreamFilter = {
1713 | onstart: (event: any) => void;
1714 | ondata: (event: { data: ArrayBuffer }) => void;
1715 | onstop: (event: any) => void;
1716 | onerror: (event: any) => void;
1717 |
1718 | close(): void;
1719 | disconnect(): void;
1720 | resume(): void;
1721 | suspend(): void;
1722 | write(data: Uint8Array | ArrayBuffer): void;
1723 |
1724 | error: string;
1725 | status:
1726 | | "uninitialized"
1727 | | "transferringdata"
1728 | | "finishedtransferringdata"
1729 | | "suspended"
1730 | | "closed"
1731 | | "disconnected"
1732 | | "failed";
1733 | };
1734 |
1735 | type HttpHeaders = (
1736 | | { name: string; binaryValue: number[]; value?: string }
1737 | | { name: string; value: string; binaryValue?: number[] }
1738 | )[];
1739 |
1740 | type BlockingResponse = {
1741 | cancel?: boolean;
1742 | redirectUrl?: string;
1743 | requestHeaders?: HttpHeaders;
1744 | responseHeaders?: HttpHeaders;
1745 | authCredentials?: { username: string; password: string };
1746 | };
1747 |
1748 | type UploadData = {
1749 | bytes?: ArrayBuffer;
1750 | file?: string;
1751 | };
1752 |
1753 | const MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES: number;
1754 |
1755 | function handlerBehaviorChanged(): Promise;
1756 |
1757 | // TODO: Enforce the return result of the addListener call in the contract
1758 | // Use an intersection type for all the default properties
1759 | interface ReqListener {
1760 | addListener: (
1761 | callback: (arg: T) => void,
1762 | filter: RequestFilter,
1763 | extraInfoSpec?: Array
1764 | ) => BlockingResponse | Promise;
1765 | removeListener: (callback: (arg: T) => void) => void;
1766 | hasListener: (callback: (arg: T) => void) => boolean;
1767 | }
1768 |
1769 | const onBeforeRequest: ReqListener<
1770 | {
1771 | requestId: string;
1772 | url: string;
1773 | method: string;
1774 | frameId: number;
1775 | parentFrameId: number;
1776 | requestBody?: {
1777 | error?: string;
1778 | formData?: { [key: string]: string[] };
1779 | raw?: UploadData[];
1780 | };
1781 | tabId: number;
1782 | type: ResourceType;
1783 | timeStamp: number;
1784 | originUrl: string;
1785 | },
1786 | "blocking" | "requestBody"
1787 | >;
1788 |
1789 | const onBeforeSendHeaders: ReqListener<
1790 | {
1791 | requestId: string;
1792 | url: string;
1793 | method: string;
1794 | frameId: number;
1795 | parentFrameId: number;
1796 | tabId: number;
1797 | type: ResourceType;
1798 | timeStamp: number;
1799 | originUrl: string;
1800 | requestHeaders?: HttpHeaders;
1801 | },
1802 | "blocking" | "requestHeaders"
1803 | >;
1804 |
1805 | const onSendHeaders: ReqListener<
1806 | {
1807 | requestId: string;
1808 | url: string;
1809 | method: string;
1810 | frameId: number;
1811 | parentFrameId: number;
1812 | tabId: number;
1813 | type: ResourceType;
1814 | timeStamp: number;
1815 | originUrl: string;
1816 | requestHeaders?: HttpHeaders;
1817 | },
1818 | "requestHeaders"
1819 | >;
1820 |
1821 | const onHeadersReceived: ReqListener<
1822 | {
1823 | requestId: string;
1824 | url: string;
1825 | method: string;
1826 | frameId: number;
1827 | parentFrameId: number;
1828 | tabId: number;
1829 | type: ResourceType;
1830 | timeStamp: number;
1831 | originUrl: string;
1832 | statusLine: string;
1833 | responseHeaders?: HttpHeaders;
1834 | statusCode: number;
1835 | },
1836 | "blocking" | "responseHeaders"
1837 | >;
1838 |
1839 | const onAuthRequired: ReqListener<
1840 | {
1841 | requestId: string;
1842 | url: string;
1843 | method: string;
1844 | frameId: number;
1845 | parentFrameId: number;
1846 | tabId: number;
1847 | type: ResourceType;
1848 | timeStamp: number;
1849 | scheme: string;
1850 | realm?: string;
1851 | challenger: { host: string; port: number };
1852 | isProxy: boolean;
1853 | responseHeaders?: HttpHeaders;
1854 | statusLine: string;
1855 | statusCode: number;
1856 | },
1857 | "blocking" | "responseHeaders"
1858 | >;
1859 |
1860 | const onResponseStarted: ReqListener<
1861 | {
1862 | requestId: string;
1863 | url: string;
1864 | method: string;
1865 | frameId: number;
1866 | parentFrameId: number;
1867 | tabId: number;
1868 | type: ResourceType;
1869 | timeStamp: number;
1870 | originUrl: string;
1871 | ip?: string;
1872 | fromCache: boolean;
1873 | statusLine: string;
1874 | responseHeaders?: HttpHeaders;
1875 | statusCode: number;
1876 | },
1877 | "responseHeaders"
1878 | >;
1879 |
1880 | const onBeforeRedirect: ReqListener<
1881 | {
1882 | requestId: string;
1883 | url: string;
1884 | method: string;
1885 | frameId: number;
1886 | parentFrameId: number;
1887 | tabId: number;
1888 | type: ResourceType;
1889 | timeStamp: number;
1890 | originUrl: string;
1891 | ip?: string;
1892 | fromCache: boolean;
1893 | statusCode: number;
1894 | redirectUrl: string;
1895 | statusLine: string;
1896 | responseHeaders?: HttpHeaders;
1897 | },
1898 | "responseHeaders"
1899 | >;
1900 |
1901 | const onCompleted: ReqListener<
1902 | {
1903 | requestId: string;
1904 | url: string;
1905 | method: string;
1906 | frameId: number;
1907 | parentFrameId: number;
1908 | tabId: number;
1909 | type: ResourceType;
1910 | timeStamp: number;
1911 | originUrl: string;
1912 | ip?: string;
1913 | fromCache: boolean;
1914 | statusCode: number;
1915 | statusLine: string;
1916 | responseHeaders?: HttpHeaders;
1917 | },
1918 | "responseHeaders"
1919 | >;
1920 |
1921 | const onErrorOccurred: ReqListener<
1922 | {
1923 | requestId: string;
1924 | url: string;
1925 | method: string;
1926 | frameId: number;
1927 | parentFrameId: number;
1928 | tabId: number;
1929 | type: ResourceType;
1930 | timeStamp: number;
1931 | originUrl: string;
1932 | ip?: string;
1933 | fromCache: boolean;
1934 | error: string;
1935 | },
1936 | void
1937 | >;
1938 |
1939 | function filterResponseData(requestId: string): StreamFilter;
1940 | }
1941 |
1942 | declare namespace browser.windows {
1943 | type WindowType = "normal" | "popup" | "panel" | "devtools";
1944 |
1945 | type WindowState = "normal" | "minimized" | "maximized" | "fullscreen" | "docked";
1946 |
1947 | type Window = {
1948 | id?: number;
1949 | focused: boolean;
1950 | top?: number;
1951 | left?: number;
1952 | width?: number;
1953 | height?: number;
1954 | tabs?: browser.tabs.Tab[];
1955 | incognito: boolean;
1956 | type?: WindowType;
1957 | state?: WindowState;
1958 | alwaysOnTop: boolean;
1959 | sessionId?: string;
1960 | };
1961 |
1962 | type CreateType = "normal" | "popup" | "panel" | "detached_panel";
1963 |
1964 | const WINDOW_ID_NONE: number;
1965 |
1966 | const WINDOW_ID_CURRENT: number;
1967 |
1968 | function get(
1969 | windowId: number,
1970 | getInfo?: {
1971 | populate?: boolean;
1972 | windowTypes?: WindowType[];
1973 | }
1974 | ): Promise;
1975 |
1976 | function getCurrent(getInfo?: { populate?: boolean; windowTypes?: WindowType[] }): Promise;
1977 |
1978 | function getLastFocused(getInfo?: {
1979 | populate?: boolean;
1980 | windowTypes?: WindowType[];
1981 | }): Promise;
1982 |
1983 | function getAll(getInfo?: { populate?: boolean; windowTypes?: WindowType[] }): Promise;
1984 |
1985 | // TODO: url and tabId should be exclusive
1986 | function create(createData?: {
1987 | allowScriptsToClose?: boolean;
1988 | url?: string | string[];
1989 | tabId?: number;
1990 | left?: number;
1991 | top?: number;
1992 | width?: number;
1993 | height?: number;
1994 | // unsupported: focused?: boolean,
1995 | incognito?: boolean;
1996 | titlePreface?: string;
1997 | type?: CreateType;
1998 | state?: WindowState;
1999 | }): Promise;
2000 |
2001 | function update(
2002 | windowId: number,
2003 | updateInfo: {
2004 | left?: number;
2005 | top?: number;
2006 | width?: number;
2007 | height?: number;
2008 | focused?: boolean;
2009 | drawAttention?: boolean;
2010 | state?: WindowState;
2011 | }
2012 | ): Promise;
2013 |
2014 | function remove(windowId: number): Promise;
2015 |
2016 | const onCreated: Listener;
2017 |
2018 | const onRemoved: Listener;
2019 |
2020 | const onFocusChanged: Listener;
2021 | }
2022 |
2023 | declare namespace browser.theme {
2024 | type Theme = {
2025 | images: ThemeImages;
2026 | colors: ThemeColors;
2027 | properties?: ThemeProperties;
2028 | };
2029 |
2030 | type ThemeImages = {
2031 | headerURL: string;
2032 | theme_frame?: string;
2033 | additional_backgrounds?: string[];
2034 | };
2035 |
2036 | type ThemeColors = {
2037 | accentcolor: string;
2038 | textcolor: string;
2039 | frame?: [number, number, number];
2040 | tab_text?: [number, number, number];
2041 | toolbar?: string;
2042 | toolbar_text?: string;
2043 | toolbar_field?: string;
2044 | toolbar_field_text?: string;
2045 | };
2046 |
2047 | type ThemeProperties = {
2048 | additional_backgrounds_alignment: Alignment[];
2049 | additional_backgrounds_tiling: Tiling[];
2050 | };
2051 |
2052 | type Alignment =
2053 | | "bottom"
2054 | | "center"
2055 | | "left"
2056 | | "right"
2057 | | "top"
2058 | | "center bottom"
2059 | | "center center"
2060 | | "center top"
2061 | | "left bottom"
2062 | | "left center"
2063 | | "left top"
2064 | | "right bottom"
2065 | | "right center"
2066 | | "right top";
2067 |
2068 | type Tiling = "no-repeat" | "repeat" | "repeat-x" | "repeat-y";
2069 |
2070 | function getCurrent(): Promise;
2071 | function getCurrent(windowId: number): Promise;
2072 | function update(theme: Theme): Promise;
2073 | function update(windowId: number, theme: Theme): Promise;
2074 | function reset(): Promise;
2075 | function reset(windowId: number): Promise;
2076 | }
2077 |
--------------------------------------------------------------------------------