|
| 1 | +import { session, ipcMain, nativeImage } from 'electron' |
| 2 | +import { EventEmitter } from 'events' |
| 3 | +import * as path from 'path' |
| 4 | + |
| 5 | +interface ExtensionAction { |
| 6 | + backgroundColor?: string |
| 7 | + text?: string |
| 8 | + title?: string |
| 9 | + icon?: |
| 10 | + | string |
| 11 | + | { |
| 12 | + path: string |
| 13 | + } |
| 14 | + popup?: { |
| 15 | + path: string |
| 16 | + } |
| 17 | +} |
| 18 | + |
| 19 | +interface ExtensionActionStore extends Partial<ExtensionAction> { |
| 20 | + tabs: { [key: string]: ExtensionAction } |
| 21 | +} |
| 22 | + |
| 23 | +export class BrowserActionAPI extends EventEmitter { |
| 24 | + sessionActionMap = new Map<Electron.Session, Map<string, ExtensionActionStore>>() |
| 25 | + |
| 26 | + constructor() { |
| 27 | + super() |
| 28 | + |
| 29 | + const setter = (propName: string) => ( |
| 30 | + event: Electron.IpcMainInvokeEvent, |
| 31 | + extensionId: string, |
| 32 | + details: chrome.browserAction.TabDetails |
| 33 | + ) => { |
| 34 | + const senderSession = event.sender.session |
| 35 | + const action = this.getAction(senderSession, extensionId) |
| 36 | + const { tabId, ...rest } = details |
| 37 | + |
| 38 | + if (details.tabId) { |
| 39 | + const tabAction = action.tabs[details.tabId] || (action.tabs[details.tabId] = {}) |
| 40 | + Object.assign(tabAction, rest) |
| 41 | + } else { |
| 42 | + Object.assign(action, rest) |
| 43 | + } |
| 44 | + } |
| 45 | + |
| 46 | + ipcMain.handle('browserAction.setBadgeBackgroundColor', setter('backgroundColor')) |
| 47 | + ipcMain.handle('browserAction.setBadgeText', setter('text')) |
| 48 | + ipcMain.handle('browserAction.setTitle', setter('title')) |
| 49 | + ipcMain.handle('browserAction.setIcon', setter('icon')) |
| 50 | + ipcMain.handle('browserAction.setPopup', setter('popup')) |
| 51 | + |
| 52 | + // extended methods for webui |
| 53 | + ipcMain.handle('browserAction.getAll', this.getAll.bind(this)) |
| 54 | + |
| 55 | + ipcMain.handle('click-action', this.onClicked.bind(this)) |
| 56 | + } |
| 57 | + |
| 58 | + private getAction(session: Electron.Session, extensionId: string) { |
| 59 | + let sessionActions = this.sessionActionMap.get(session) |
| 60 | + if (!sessionActions) { |
| 61 | + sessionActions = new Map() |
| 62 | + this.sessionActionMap.set(session, sessionActions) |
| 63 | + } |
| 64 | + |
| 65 | + let action = sessionActions.get(extensionId) |
| 66 | + if (!action) { |
| 67 | + action = { tabs: {} } |
| 68 | + sessionActions.set(extensionId, action) |
| 69 | + } |
| 70 | + |
| 71 | + return action |
| 72 | + } |
| 73 | + |
| 74 | + private processIcon(extension: Electron.Extension) { |
| 75 | + const { browser_action } = extension.manifest |
| 76 | + const { default_icon } = browser_action |
| 77 | + |
| 78 | + if (typeof default_icon === 'string') { |
| 79 | + const iconPath = path.join(extension.path, default_icon) |
| 80 | + const image = nativeImage.createFromPath(iconPath) |
| 81 | + return image.toDataURL() |
| 82 | + } else if (typeof default_icon === 'object') { |
| 83 | + const key = Object.keys(default_icon).pop() as any |
| 84 | + const iconPath = path.join(extension.path, default_icon[key]) |
| 85 | + const image = nativeImage.createFromPath(iconPath) |
| 86 | + return image.toDataURL() |
| 87 | + } |
| 88 | + } |
| 89 | + |
| 90 | + getPopupPath(session: Electron.Session, extensionId: string, tabId: string) { |
| 91 | + const action = this.getAction(session, extensionId) |
| 92 | + return action.tabs[tabId]?.popup?.path |
| 93 | + } |
| 94 | + |
| 95 | + processExtensions(session: Electron.Session, extensions: Electron.Extension[]) { |
| 96 | + const populate = (extension: Electron.Extension) => { |
| 97 | + const manifest = extension.manifest as chrome.runtime.Manifest |
| 98 | + const { browser_action } = manifest |
| 99 | + if (browser_action) { |
| 100 | + const action = this.getAction(session, extension.id) |
| 101 | + |
| 102 | + action.title = browser_action.default_title || manifest.name |
| 103 | + |
| 104 | + const icon = this.processIcon(extension) |
| 105 | + if (icon) action.icon = icon |
| 106 | + } |
| 107 | + } |
| 108 | + |
| 109 | + extensions.forEach(populate) |
| 110 | + } |
| 111 | + |
| 112 | + private getAll(event: Electron.IpcMainInvokeEvent) { |
| 113 | + const senderSession = event.sender.session || session.defaultSession |
| 114 | + let sessionActions = this.sessionActionMap.get(senderSession) |
| 115 | + if (!sessionActions) return [] |
| 116 | + |
| 117 | + return Array.from(sessionActions.entries()).map((val: any) => ({ id: val[0], ...val[1] })) |
| 118 | + } |
| 119 | + |
| 120 | + private onClicked(event: Electron.IpcMainInvokeEvent, extensionId: string) { |
| 121 | + this.emit('clicked', event, extensionId) |
| 122 | + } |
| 123 | +} |
0 commit comments