diff --git a/shared/js/background/components/debugger-connection.js b/shared/js/background/components/debugger-connection.js index 7ed0524781..59592a3142 100644 --- a/shared/js/background/components/debugger-connection.js +++ b/shared/js/background/components/debugger-connection.js @@ -1,5 +1,8 @@ +import browser from 'webextension-polyfill' import { registerMessageHandler } from '../message-handlers' -import { getFromSessionStorage, removeFromSessionStorage, setToSessionStorage } from '../wrapper' +import { getBrowserName } from '../utils' +import { getExtensionVersion, getFromSessionStorage, removeFromSessionStorage, setToSessionStorage } from '../wrapper' +import { registerDebugHandler } from '../devtools' /** * @typedef {import('./tds').default} TDSStorage @@ -25,6 +28,8 @@ export default class DebuggerConnection { constructor ({ tds }) { this.init() this.tds = tds + this.socket = null + this.subscribedTabs = new Set() registerMessageHandler('getDebuggingSettings', getDebuggerSettings) registerMessageHandler('enableDebugging', ({ configURLOverride, debuggerConnection }) => { return this.enableDebugging(configURLOverride, debuggerConnection) @@ -38,20 +43,75 @@ export default class DebuggerConnection { this.configURLOverride = configURLOverride this.debuggerConnectionEnabled = debuggerConnection if (this.configURLOverride && this.debuggerConnectionEnabled) { - const url = new URL('./status', this.configURLOverride) + const url = new URL('./debugger/extension', this.configURLOverride.replace(/https?:/, 'ws:')) + url.searchParams.append('browserName', getBrowserName()) + url.searchParams.append('version', getExtensionVersion()) let lastUpdate = 0 - this.eventSource = new EventSource(url.href) - this.eventSource.onmessage = event => { - const status = JSON.parse(event.data) - console.log('debugger message', status) - if (status.lastBuild > lastUpdate) { - lastUpdate = status.lastBuild - this.tds.config.checkForUpdates(true) + this.socket = new WebSocket(url.href) + this.socket.addEventListener('message', (event) => { + const { messageType, payload } = JSON.parse(event.data) + console.log('debugger message', event.data) + if (messageType === 'status') { + if (payload.lastBuild > lastUpdate) { + lastUpdate = payload.lastBuild + this.tds.config.checkForUpdates(true) + } + } else if (messageType === 'subscribe') { + const { tabId } = payload + if (!this.subscribedTabs.has(tabId)) { + this.subscribedTabs.add(tabId) + this.forwardDebugMessagesForTab(tabId) + } + } else if (messageType === 'reloadTab') { + const { tabId } = payload + browser.tabs.reload(tabId) } + }) + this.socket.addEventListener('close', () => { + this.socket = null + setTimeout(() => this.init(), 5000) + }) + + // rate limit sending tabs to 1 message per second + let lastTabSend = 0 + let nextTabSend = null + const sendTabs = async () => { + if (nextTabSend) { + return + } + if (Date.now() - lastTabSend < 1000) { + nextTabSend = setTimeout(() => { + nextTabSend = null + sendTabs() + }, 1000) + return + } + lastTabSend = Date.now() + const tabs = await browser.tabs.query({}) + this.socket?.send(JSON.stringify({ + messageType: 'tabs', + payload: tabs.sort((a, b) => (a.lastAccessed || 0) - (b.lastAccessed || 0)) + })) } + + this.socket.addEventListener('open', async () => { + sendTabs() + + browser.tabs.onUpdated.addListener(() => { + sendTabs() + }) + + this.subscribedTabs.forEach((tabId) => { + this.forwardDebugMessagesForTab(tabId) + }) + }) } } + async isActive () { + return this.socket !== null + } + async enableDebugging (url, debuggerConnection = false) { await Promise.all([ setToSessionStorage('configURLOverride', url), @@ -70,6 +130,17 @@ export default class DebuggerConnection { removeFromSessionStorage('configURLOverride'), removeFromSessionStorage('debuggerConnection') ]) - this.eventSource?.close() + this.socket?.close() + } + + forwardDebugMessagesForTab (tabId) { + registerDebugHandler(tabId, (payload) => { + if (this.socket) { + this.socket.send(JSON.stringify({ + messageType: 'devtools', + payload + })) + } + }) } } diff --git a/shared/js/background/components/internal-user-detector.js b/shared/js/background/components/internal-user-detector.js index 4594f2e0eb..4c294ff966 100644 --- a/shared/js/background/components/internal-user-detector.js +++ b/shared/js/background/components/internal-user-detector.js @@ -1,3 +1,4 @@ +/* global DEBUG */ import browser from 'webextension-polyfill' import { registerMessageHandler } from '../message-handlers' @@ -9,7 +10,7 @@ const SETTINGS_KEY = 'isInternalUser' * @param {import('../settings.js')} settings */ export function isInternalUser (settings) { - return settings.getSetting(SETTINGS_KEY) || false + return settings.getSetting(SETTINGS_KEY) || DEBUG } export default class InternalUserDetector { diff --git a/shared/js/background/devtools.js b/shared/js/background/devtools.js index cefab2afb4..6e7c8e69cb 100644 --- a/shared/js/background/devtools.js +++ b/shared/js/background/devtools.js @@ -13,11 +13,16 @@ const { removeBroken } = require('./utils') // background ServiceWorker will become active again and the onConnect // event will fire again. const ports = new Map() +const debugHandlers = new Map() export function init () { browser.runtime.onConnect.addListener(connected) } +export function registerDebugHandler (tabId, fn) { + debugHandlers.set(tabId, fn) +} + /** * Serialize a subset of the tab object to be sent to the panel * @param {Object} tab @@ -131,8 +136,11 @@ export function postMessage (tabId, action, message) { if (ports.has(tabId)) { ports.get(tabId).postMessage(JSON.stringify({ tabId, action, message })) } + if (debugHandlers.has(tabId)) { + debugHandlers.get(tabId)({ tabId, action, message }) + } } export function isActive (tabId) { - return ports.has(tabId) + return ports.has(tabId) || debugHandlers.has(tabId) }