Skip to content

Commit

Permalink
fix: prevent batch call creates too much popup windows (#11669)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jack-Works authored Jun 12, 2024
1 parent 6332b32 commit 3b25bf9
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 58 deletions.
2 changes: 1 addition & 1 deletion packages/mask/background/services/helper/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export {
removePopupWindow,
openDashboard,
queryCurrentActiveTab,
queryCurrentPopupWindowId,
hasPopupWindowOpened,
} from './popup-opener.js'
export {
queryExtensionPermission,
Expand Down
102 changes: 52 additions & 50 deletions packages/mask/background/services/helper/popup-opener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,63 +2,64 @@ import urlcat, { type ParamMap } from 'urlcat'
import { type DashboardRoutes, PopupRoutes, MaskMessages, type PopupRoutesParamsMap } from '@masknet/shared-base'
import { isLocked } from '../wallet/services/index.js'

let currentPopupWindowId = 0
let currentPopupWindowIdPromise: Promise<number | undefined> | undefined

async function openWindow(url: string): Promise<void> {
const windows = await browser.windows.getAll()
const popup = windows.find((window) => window.type === 'popup' && window.id === currentPopupWindowId)
if (popup) {
await browser.windows.update(popup.id!, { focused: true })
async function openOrFocusPopupWindow(initialURL: string): Promise<void> {
const currentId = await currentPopupWindowIdPromise
if (currentId) {
await browser.windows.update(currentId, { focused: true })
} else {
let left: number
let top: number
currentPopupWindowIdPromise = (async () => {
try {
const lastFocused = await browser.windows.getLastFocused()
// Position window in top right corner of lastFocused window.
top = lastFocused.top ?? 0
left = (lastFocused.left ?? 0) + (lastFocused.width ?? 0) - 400
} catch {
// The following properties are more than likely 0, due to being
// opened from the background chrome process for the extension that
// has no physical dimensions

try {
const lastFocused = await browser.windows.getLastFocused()
// Position window in top right corner of lastFocused window.
top = lastFocused.top ?? 0
left = (lastFocused.left ?? 0) + (lastFocused.width ?? 0) - 400
} catch {
// The following properties are more than likely 0, due to being
// opened from the background chrome process for the extension that
// has no physical dimensions

// Note: DOM is only available in MV2 or MV3 page mode.
const { screenX, outerWidth, screenY } = globalThis as any
if (typeof screenX === 'number' && typeof screenY === 'number' && typeof outerWidth === 'number') {
top = Math.max(screenY, 0)
left = Math.max(screenX + (outerWidth - 400), 0)
} else {
top = 100
left = 100
// Note: DOM is only available in MV2 or MV3 page mode.
const { screenX, outerWidth, screenY } = globalThis as any
if (typeof screenX === 'number' && typeof screenY === 'number' && typeof outerWidth === 'number') {
top = Math.max(screenY, 0)
left = Math.max(screenX + (outerWidth - 400), 0)
} else {
top = 100
left = 100
}
}
}

const { id } = await browser.windows.create({
url: browser.runtime.getURL(url),
width: 400,
height: 628,
type: 'popup',
state: 'normal',
left,
top,
})

// update currentPopupWindowId and clean event
if (id) {
currentPopupWindowId = id
browser.windows.onRemoved.addListener(function listener(windowID: number) {
if (windowID !== id) return
currentPopupWindowId = 0
browser.windows.onRemoved.removeListener(listener)
const { id } = await browser.windows.create({
url: browser.runtime.getURL(initialURL),
width: 400,
height: 628,
type: 'popup',
state: 'normal',
left,
top,
})
}

// update currentPopupWindowId and clean event
if (id) {
browser.windows.onRemoved.addListener(function listener(windowID: number) {
if (windowID !== id) return
currentPopupWindowIdPromise = undefined
browser.windows.onRemoved.removeListener(listener)
})
}
return id
})()
}
}
async function openOrUpdatePopupWindow(route: PopupRoutes, params: ParamMap) {
if (!currentPopupWindowId) return openWindow(urlcat('popups.html#', route, params))
const currentId = await currentPopupWindowIdPromise
if (!currentId) return openOrFocusPopupWindow(urlcat('popups.html#', route, params))

await browser.windows.update(currentPopupWindowId, { focused: true })
await browser.windows.update(currentId, { focused: true })
MaskMessages.events.popupRouteUpdated.sendToAll(
urlcat(route, {
close_after_unlock: true,
Expand Down Expand Up @@ -94,14 +95,15 @@ export async function openPopupWindow<T extends PopupRoutes>(
}
}

export async function queryCurrentPopupWindowId() {
return currentPopupWindowId
export async function hasPopupWindowOpened(): Promise<boolean | undefined> {
return currentPopupWindowIdPromise?.then((id) => !!id)
}

export async function removePopupWindow(): Promise<void> {
if (!currentPopupWindowId) return
browser.windows.remove(currentPopupWindowId)
currentPopupWindowId = 0
const currentId = await currentPopupWindowIdPromise
if (!currentId) return
browser.windows.remove(currentId)
currentPopupWindowIdPromise = undefined
}

export async function openDashboard(route: DashboardRoutes, search?: string) {
Expand Down
16 changes: 11 additions & 5 deletions packages/mask/background/services/wallet/services/wallet/locker.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CrossIsolationMessages, PopupRoutes } from '@masknet/shared-base'
import { getAutoLockerDuration } from './database/locker.js'
import * as password from './password.js'
import { openPopupWindow } from '../../../helper/popup-opener.js'
import { hasPopupWindowOpened, openPopupWindow } from '../../../helper/popup-opener.js'

export async function isLocked() {
return (await password.hasPassword()) && !(await password.hasVerifiedPassword())
Expand All @@ -25,14 +25,20 @@ export async function unlockWallet(unverifiedPassword: string) {
}
}

let pendingRequest: Promise<void> | undefined
export async function requestUnlockWallet(): Promise<void> {
if (!(await isLocked())) return
await openPopupWindow(PopupRoutes.WalletUnlock, {})
return new Promise((resolve) => {
CrossIsolationMessages.events.walletLockStatusUpdated.on((locked) => {
if (!locked) resolve()

if (!(await hasPopupWindowOpened())) await openPopupWindow(PopupRoutes.WalletUnlock, {})
pendingRequest ??= new Promise((resolve) => {
const removeListener = CrossIsolationMessages.events.walletLockStatusUpdated.on((locked) => {
if (locked) return
resolve()
removeListener()
pendingRequest = undefined
})
})
return pendingRequest
}

// This setTimeout is ok because if the background worker is killed,
Expand Down
3 changes: 1 addition & 2 deletions packages/mask/popups/components/SelectProvider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@ export const SelectProvider = memo(function SelectProvider() {
return
}
} else {
const currentPopupWindowId = await Services.Helper.queryCurrentPopupWindowId()
if (currentPopupWindowId) {
if (await Services.Helper.hasPopupWindowOpened()) {
modalNavigate(PopupModalRoutes.ConnectProvider, { providerType })
return
}
Expand Down

0 comments on commit 3b25bf9

Please sign in to comment.