(source, currentUrl, options = {})
| 58 | } |
| 59 | |
| 60 | async function closeConflictingTabsForSource(source, currentUrl, options = {}) { |
| 61 | const { excludeTabIds = [] } = options; |
| 62 | const excluded = new Set(excludeTabIds.filter((id) => Number.isInteger(id))); |
| 63 | const state = await getState(); |
| 64 | const lastUrl = state.sourceLastUrls?.[source]; |
| 65 | const referenceUrls = [currentUrl, lastUrl].filter(Boolean); |
| 66 | |
| 67 | if (!referenceUrls.length) return; |
| 68 | |
| 69 | const tabs = await chrome.tabs.query({}); |
| 70 | const matchedIds = tabs |
| 71 | .filter((tab) => Number.isInteger(tab.id) && !excluded.has(tab.id)) |
| 72 | .filter((tab) => referenceUrls.some((refUrl) => matchesSourceUrlFamily(source, tab.url, refUrl))) |
| 73 | .map((tab) => tab.id); |
| 74 | |
| 75 | if (!matchedIds.length) return; |
| 76 | |
| 77 | await chrome.tabs.remove(matchedIds).catch(() => { }); |
| 78 | |
| 79 | const registry = await getTabRegistry(); |
| 80 | if (registry[source]?.tabId && matchedIds.includes(registry[source].tabId)) { |
| 81 | registry[source] = null; |
| 82 | await setState({ tabRegistry: registry }); |
| 83 | } |
| 84 | |
| 85 | await addLog(`已关闭 ${matchedIds.length} 个旧的${getSourceLabel(source)}标签页。`, 'info'); |
| 86 | } |
| 87 | |
| 88 | function isLocalhostOAuthCallbackTabMatch(callbackUrl, candidateUrl) { |
| 89 | if (!isLocalhostOAuthCallbackUrl(callbackUrl) || !isLocalhostOAuthCallbackUrl(candidateUrl)) { |
no test coverage detected