main
ts 136 lines 3.72 KB
Raw
1 import { SOURCE_CLIENT } from '@ipshipyard/libp2p-inspector-metrics'
2 import { SOURCE_SERVICE_WORKER } from './constants.js'
3 import { getBrowserInstance } from './utils/get-browser.js'
4 import type { DevToolsMessage, PageLoadedMessage, WorkerMessage } from './app.tsx'
5
6 const browser = getBrowserInstance()
7
8 /**
9 * Holds connections between tab id -> devTools
10 */
11 const toDevTools: Record<number, chrome.runtime.Port> = {}
12
13 /**
14 * Holds connections between tab id -> browser
15 */
16 const toContentScript: Record<number, chrome.runtime.Port> = {}
17
18 /**
19 * Listen for incoming connections from the dev tools panel, when a message is
20 * received, open a port to the content script and forward the message.
21 *
22 * Listen for incoming messages on the forwarding port and relay them back to
23 * the dev tools panel.
24 */
25 browser.runtime.onConnect.addListener(port => {
26 // only accept incoming connections from the dev tools panel
27 if (port.name !== SOURCE_CLIENT) {
28 return
29 }
30
31 let tabId: number | undefined
32
33 const onContentScriptMessage = (message: WorkerMessage): void => {
34 port.postMessage(message)
35 }
36
37 const onDevToolsPanelMessage = (message: DevToolsMessage): void => {
38 if (tabId == null) {
39 tabId = message.tabId
40 }
41
42 if (toDevTools[tabId] !== port) {
43 toDevTools[tabId]?.onMessage.removeListener(onDevToolsPanelMessage)
44 toDevTools[tabId] = port
45 }
46
47 if (toContentScript[tabId] == null) {
48 toContentScript[tabId] = browser.tabs.connect(message.tabId, {
49 name: SOURCE_SERVICE_WORKER
50 })
51
52 toContentScript[tabId].onMessage.addListener(onContentScriptMessage)
53
54 toContentScript[tabId].onDisconnect.addListener((port) => {
55 port.onMessage.removeListener(onContentScriptMessage)
56
57 delete toContentScript[message.tabId]
58 })
59 }
60
61 // intercept copy-to-clipboard message
62 if (message.type === 'copy-to-clipboard') {
63 copyToClipboard(tabId, message.value); return
64 }
65
66 // forward message to content script
67 toContentScript[tabId].postMessage(message)
68 }
69
70 // Listen to messages sent from the DevTools page
71 port.onMessage.addListener(onDevToolsPanelMessage)
72
73 // if the dev tools panel disconnects, clean up references - we will reconnect
74 // if the dev tools panel is reloaded
75 port.onDisconnect.addListener(() => {
76 // do not process messages on a closed port
77 port.onMessage.removeListener(onDevToolsPanelMessage)
78
79 // clean up ports
80 if (tabId != null) {
81 // stop processing content script messages
82 toContentScript[tabId]?.onMessage.removeListener(onContentScriptMessage)
83
84 delete toContentScript[tabId]
85
86 delete toDevTools[tabId]
87 }
88 })
89 })
90
91 browser.tabs?.onUpdated.addListener((tabId, changeInfo): void => {
92 if (changeInfo.status !== 'complete') {
93 return
94 }
95
96 const port = toDevTools[tabId]
97
98 if (port != null) {
99 const message: PageLoadedMessage = {
100 source: SOURCE_CLIENT,
101 type: 'page-loaded',
102 tabId
103 }
104
105 port.postMessage(message)
106 }
107 })
108
109 function copyToClipboard (tabId: number, text: string): void {
110 function contentCopy (text: string): void {
111 const input = document.createElement('textarea')
112
113 // position absolutely to stop the page from jumping when we call .focus()
114 input.style.position = 'absolute'
115 input.style.top = `${window.scrollY}px`
116 input.style.left = `${window.scrollX}px`
117
118 document.body.appendChild(input)
119 input.value = text
120 input.focus()
121 input.select()
122 document.execCommand('copy')
123 input.remove()
124 }
125
126 // execute the content script
127 chrome.scripting.executeScript({
128 target: { tabId },
129 func: contentCopy,
130 args: [text]
131 })
132 .catch(err => {
133 // eslint-disable-next-line no-console
134 console.error('error executing copy script', err)
135 })
136 }