Skip to content

Commit

Permalink
feat: handle stale dApp requests
Browse files Browse the repository at this point in the history
  • Loading branch information
xstelea committed May 29, 2023
1 parent 1c04f86 commit 7f616ec
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 12 deletions.
13 changes: 12 additions & 1 deletion src/chrome/background/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,19 @@ const handleConnectionPasswordChange = (connectionPassword?: string) =>
}, config.popup.closeDelayTime)
})

const tabRemovedListener = (tabId: number) =>
const tabRemovedListener = (tabId: number) => {
ledgerTabWatcher.triggerTabRemoval(tabId)
messageHandler.sendMessageAndWaitForConfirmation(
createMessage.closeDappTab('background', tabId)
)
}

chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
const isTabReload = changeInfo.status === 'loading' && !changeInfo.url
if (isTabReload) {
tabRemovedListener(tabId)
}
})

chrome.runtime.onMessage.addListener((message, sender) => {
messageHandler.onMessage(message, sender.tab?.id)
Expand Down
3 changes: 2 additions & 1 deletion src/chrome/dev-tools/components/LedgerSimulator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ const signingConfig = {
const getLocalStorageMnemonics = () => {
try {
const mnemonics =
JSON.parse(localStorage.getItem('radix-dev-tools-mnemonics') ?? '[]') || []
JSON.parse(localStorage.getItem('radix-dev-tools-mnemonics') ?? '[]') ||
[]
return mnemonics
} catch (e) {
return []
Expand Down
5 changes: 5 additions & 0 deletions src/chrome/messages/_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const messageDiscriminator = {
setConnectionPassword: 'setConnectionPassword',
dAppRequest: 'dAppRequest',
closeLedgerTab: 'closeLedgerTab',
closeDappTab: 'closeDappTab',
ledgerResponse: 'ledgerResponse',
walletToLedger: 'walletToLedger',
convertPopupToTab: 'convertPopupToTab',
Expand Down Expand Up @@ -107,6 +108,10 @@ export type Messages = {
MessageDiscriminator['closeLedgerTab'],
{}
>
[messageDiscriminator.closeDappTab]: MessageBuilder<
MessageDiscriminator['closeDappTab'],
{ tabId: number }
>
[messageDiscriminator.incomingDappMessage]: MessageBuilder<
MessageDiscriminator['incomingDappMessage'],
{ data: Record<string, any> }
Expand Down
9 changes: 9 additions & 0 deletions src/chrome/messages/create-message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ export const createMessage = {
discriminator: 'closeLedgerTab',
messageId: crypto.randomUUID(),
}),
closeDappTab: (
source: MessageSource,
tabId: number
): Messages['closeDappTab'] => ({
source,
tabId,
discriminator: 'closeDappTab',
messageId: crypto.randomUUID(),
}),
walletToLedger: (
source: MessageSource,
message: LedgerRequest
Expand Down
4 changes: 2 additions & 2 deletions src/chrome/messages/message-client.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ describe('message client', () => {
// so it has to proxy the message through background message handler
it('should send wallet response to dApp', async () => {
const testHelper = createTestHelper({})
testHelper.messageRouter.add(1, '456')
testHelper.messageRouter.add(1, '456', '')
testHelper.mockIncomingWalletMessage({ interactionId: '456' }, 1)

await Promise.all([
Expand Down Expand Up @@ -192,7 +192,7 @@ describe('message client', () => {
),
})

testHelper.messageRouter.add(1, '456')
testHelper.messageRouter.add(1, '456', '')
testHelper.mockIncomingWalletMessage({ interactionId: '456' }, 1)

await Promise.all([
Expand Down
22 changes: 19 additions & 3 deletions src/chrome/offscreen/message-handler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createMessage } from 'chrome/messages/create-message'
import { ConnectorClient } from 'connector/connector-client'
import { MessagesRouter } from 'message-router'
import { errAsync, okAsync } from 'neverthrow'
import { ResultAsync, errAsync, okAsync } from 'neverthrow'
import { Queue } from 'queues/queue'
import { AppLogger, logger as appLogger } from 'utils/logger'
import {
Expand Down Expand Up @@ -64,9 +64,9 @@ export const OffscreenMessageHandler = (input: {
}

case messageDiscriminator.dAppRequest: {
const { interactionId } = message.data
const { interactionId, metadata } = message.data
return messageRouter
.add(tabId!, interactionId)
.add(tabId!, interactionId, metadata.origin)
.asyncAndThen(() => {
if (message.data?.items?.discriminator === 'cancelRequest')
return dAppRequestQueue
Expand Down Expand Up @@ -104,6 +104,22 @@ export const OffscreenMessageHandler = (input: {
.add(message.data, message.data.interactionId)
.map(() => ({ sendConfirmation: false }))

case messageDiscriminator.closeDappTab: {
const { tabId } = message
return messageRouter
.getAndRemoveByTabId(tabId)
.mapErr(() => ({ reason: 'tabIdNotFound' }))
.map((interactionIds) => {
for (const interactionId of interactionIds) {
ResultAsync.combine([
dAppRequestQueue.cancel(interactionId),
incomingWalletMessageQueue.cancel(interactionId),
])
}
})
.map(() => ({ sendConfirmation: false }))
}

default:
return errAsync({
reason: 'unhandledMessageDiscriminator',
Expand Down
36 changes: 31 additions & 5 deletions src/message-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,47 @@ import { AppLogger } from 'utils/logger'
export type MessagesRouter = ReturnType<typeof MessagesRouter>

export const MessagesRouter = ({ logger }: { logger: AppLogger }) => {
const store = new Map<string, number>()
const store = new Map<string, { tabId: number; origin: string }>()

const add = (tabId: number, interactionId: string) => {
store.set(interactionId, tabId)
const add = (tabId: number, interactionId: string, origin: string) => {
store.set(interactionId, { tabId, origin })
return ok(undefined)
}

const getTabId = (interactionId: string): ResultAsync<number, Error> => {
const tabId = store.get(interactionId)
return tabId ? okAsync(tabId) : errAsync(new Error('No tab found'))
const values = store.get(interactionId)
return values ? okAsync(values.tabId) : errAsync(new Error('No tab found'))
}

const getInteractionIdsByTabId = (
tabId: number
): ResultAsync<string[], Error> => {
const interactionIds = [...store.entries()]
.filter(([, value]) => value.tabId === tabId)
.map(([key]) => key)

return interactionIds.length
? okAsync(interactionIds)
: errAsync(new Error('No interactionId found'))
}

const removeByTabId = (tabId: number) =>
getInteractionIdsByTabId(tabId).map((interactionIds) =>
interactionIds.forEach((interactionId) => store.delete(interactionId))
)

const getAndRemoveByTabId = (tabId: number) =>
getInteractionIdsByTabId(tabId).map((interactionIds) => {
interactionIds.forEach((interactionId) => store.delete(interactionId))
return interactionIds
})

return {
add,
getTabId,
store,
getInteractionIdsByTabId,
removeByTabId,
getAndRemoveByTabId,
}
}

0 comments on commit 7f616ec

Please sign in to comment.