203 lines
6.7 KiB
TypeScript
203 lines
6.7 KiB
TypeScript
import { browser, Menus, Runtime } from "webextension-polyfill-ts";
|
|
import { setPlatform } from "@padloc/core/src/platform";
|
|
import { App } from "@padloc/core/src/app";
|
|
import { bytesToBase64, base64ToBytes } from "@padloc/core/src/encoding";
|
|
import { AjaxSender } from "@padloc/app/src/lib/ajax";
|
|
import { debounce } from "@padloc/core/src/util";
|
|
import { ExtensionPlatform } from "./platform";
|
|
import { Message, messageTab } from "./message";
|
|
|
|
setPlatform(new ExtensionPlatform());
|
|
|
|
class ExtensionBackground {
|
|
app = new App(new AjaxSender(process.env.PL_SERVER_URL!));
|
|
|
|
// private _currentItemIndex = -1;
|
|
|
|
private _reload = debounce(() => this.app.reload(), 30000);
|
|
|
|
async init() {
|
|
this.app.subscribe(() => this._stateChanged());
|
|
const update = debounce(() => this._update(), 500);
|
|
browser.runtime.onMessage.addListener(async (msg: Message, sender: Runtime.MessageSender) => {
|
|
if (sender.tab) {
|
|
// Communication with content-scripts is one-way, to we ignore
|
|
// messages from them, just to be safe
|
|
return;
|
|
}
|
|
|
|
switch (msg.type) {
|
|
case "loggedOut":
|
|
case "locked":
|
|
await this.app.load();
|
|
update();
|
|
break;
|
|
case "unlocked":
|
|
await this.app.load();
|
|
await this.app.unlockWithMasterKey(base64ToBytes(msg.masterKey));
|
|
update();
|
|
break;
|
|
case "requestMasterKey":
|
|
return (
|
|
(this.app.account && this.app.account.masterKey && bytesToBase64(this.app.account.masterKey)) ||
|
|
null
|
|
);
|
|
// case "calcTOTP":
|
|
// return totp(base32ToBytes(msg.secret));
|
|
}
|
|
});
|
|
browser.tabs.onUpdated.addListener(update);
|
|
browser.tabs.onActivated.addListener(update);
|
|
|
|
browser.contextMenus.onClicked.addListener(({ menuItemId }: Menus.OnClickData) =>
|
|
this._contextMenuClicked(menuItemId as string)
|
|
);
|
|
|
|
this.app.load();
|
|
// browser.commands.onCommand.addListener(command => this._executeCommand(command));
|
|
}
|
|
|
|
private async _getActiveTab() {
|
|
const [tab] = await browser.tabs.query({ currentWindow: true, active: true });
|
|
return tab || null;
|
|
}
|
|
|
|
private async _contextMenuClicked(menuItemId: string) {
|
|
if (menuItemId === "openPopup") {
|
|
browser.browserAction.openPopup();
|
|
return;
|
|
}
|
|
|
|
const match = menuItemId.match(/^item\/([^\/]+)(?:\/(\d+))?$/);
|
|
|
|
if (!match) {
|
|
return;
|
|
}
|
|
|
|
const [, id, ind] = match;
|
|
const item = this.app.getItem(id);
|
|
const index = parseInt(ind);
|
|
if (!item || isNaN(index)) {
|
|
return;
|
|
}
|
|
|
|
const field = item.item.fields[index];
|
|
const value = await field.transform();
|
|
await messageTab({
|
|
type: "fillActive",
|
|
value,
|
|
});
|
|
|
|
// this._openItem(item.item, index ? parseInt(index) : undefined);
|
|
}
|
|
|
|
// private _openItem(item: VaultItem, index?: number) {
|
|
// messageTab({
|
|
// type: "autoFill",
|
|
// item,
|
|
// index
|
|
// });
|
|
// }
|
|
|
|
// private async _executeCommand(command: string) {
|
|
// const items = await this._getItemsForTab();
|
|
//
|
|
// switch (command) {
|
|
// case "open-next":
|
|
// this._currentItemIndex = (this._currentItemIndex + 1) % items.length;
|
|
// if (items[this._currentItemIndex]) {
|
|
// this._openItem(items[this._currentItemIndex].item);
|
|
// }
|
|
// break;
|
|
// case "open-previous":
|
|
// this._currentItemIndex = (this._currentItemIndex + items.length - 1) % items.length;
|
|
// if (items[this._currentItemIndex]) {
|
|
// this._openItem(items[this._currentItemIndex].item);
|
|
// }
|
|
// break;
|
|
// }
|
|
// }
|
|
|
|
private async _updateBadge() {
|
|
const count = await this._getCountForTab();
|
|
browser.browserAction.setBadgeText({ text: count ? count.toString() : "" });
|
|
browser.browserAction.setBadgeBackgroundColor({ color: "#ff6666" });
|
|
}
|
|
|
|
private async _getItemsForTab() {
|
|
const tab = await this._getActiveTab();
|
|
return tab && tab.url ? this.app.getItemsForUrl(tab.url) : [];
|
|
}
|
|
|
|
private async _getCountForTab() {
|
|
const tab = await this._getActiveTab();
|
|
return tab && tab.url ? await this.app.state.index.matchUrl(tab.url) : 0;
|
|
}
|
|
|
|
private async _updateContextMenu() {
|
|
await browser.contextMenus.removeAll();
|
|
|
|
const openPopupAvailable = typeof browser.browserAction.openPopup === "function";
|
|
const count = await this._getCountForTab();
|
|
|
|
if (!count || !this.app.state.loggedIn) {
|
|
return;
|
|
}
|
|
|
|
if (this.app.state.locked) {
|
|
await browser.contextMenus.create({
|
|
id: "openPopup",
|
|
title: `${count > 1 ? `${count} items` : "1 item"} found${
|
|
!openPopupAvailable ? " (unlock to view)" : ""
|
|
}`,
|
|
enabled: openPopupAvailable,
|
|
contexts: ["editable"],
|
|
});
|
|
} else {
|
|
const items = await this._getItemsForTab();
|
|
for (const { item } of items) {
|
|
await browser.contextMenus.create({
|
|
id: `item/${item.id}`,
|
|
title: item.name,
|
|
contexts: ["editable"],
|
|
});
|
|
|
|
for (const [index, field] of item.fields.entries()) {
|
|
await browser.contextMenus.create({
|
|
parentId: `item/${item.id}`,
|
|
id: `item/${item.id}/${index}`,
|
|
title: field.name,
|
|
contexts: ["editable"],
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private async _update() {
|
|
this._updateBadge();
|
|
this._updateContextMenu();
|
|
// this._currentItemIndex = -1;
|
|
}
|
|
|
|
private _stateChanged() {
|
|
this._reload();
|
|
this._updateIcon();
|
|
}
|
|
|
|
private _updateIcon() {
|
|
if (!this.app.account) {
|
|
browser.browserAction.setIcon({ path: "icon-grayscale.png" });
|
|
browser.browserAction.setTitle({ title: "Please Log In" });
|
|
} else {
|
|
browser.browserAction.setIcon({ path: "icon.png" });
|
|
browser.browserAction.setTitle({ title: "Padloc" });
|
|
}
|
|
}
|
|
}
|
|
|
|
//@ts-ignore
|
|
const extension = (window.extension = new ExtensionBackground());
|
|
|
|
extension.init();
|