add isolated tab feature

This commit is contained in:
Fabian Freund
2026-02-27 08:29:50 +01:00
parent 3ef567cc19
commit d24675094d
84 changed files with 3783 additions and 513 deletions
@@ -93,8 +93,38 @@ export class Store {
return this.siteAssignments.has(uri.origin)
}
/**
* Returns the effective proxy relation for a context ID, mirroring the
* fallback logic in getProxiesForContainer: explicit relation first,
* then 'general' for non-private contexts, then empty.
*/
private getEffectiveRelation(contextId: string): string[] {
return this.relations[contextId]
?? ((contextId !== 'private') ? this.relations['general'] : undefined)
?? [];
}
isSiteOriginInSameContext(uri: URL, contextId: string): boolean {
return this.siteAssignments.get(uri.origin) === contextId;
const assignedContextId = this.siteAssignments.get(uri.origin);
if (assignedContextId === undefined) return false;
if (assignedContextId === contextId) return true;
// Context equivalence: compare effective proxy relations (including
// fallback to 'general') so that isolated tabs in non-proxied containers
// or containers relying on the general relation are treated as compatible.
const assignedRelation = this.getEffectiveRelation(assignedContextId);
const currentRelation = this.getEffectiveRelation(contextId);
// Only treat as equivalent if both have actual proxy relations —
// empty relations mean no proxy, and different non-proxied contexts
// should not be considered equivalent.
if (assignedRelation.length > 0 &&
assignedRelation.length === currentRelation.length &&
assignedRelation.every((id, i) => id === currentRelation[i])) {
return true;
}
return false;
}
getAllProxies(): ProxySettings[] {
@@ -164,4 +164,31 @@ describe('Store', () => {
expect(relations.container2).to.be.deep.equal(['proxy2'])
})
})
describe('isSiteOriginInSameContext', function () {
it('should allow proxy-equivalent isolated context', async () => {
store.setSiteAssignments(new Map([['https://example.com/page', 'container_ctx']]))
await store.setContainerProxyRelation('container_ctx', 'proxy_ctx')
await store.setContainerProxyRelation('iso1_ctx', 'proxy_ctx')
const result = store.isSiteOriginInSameContext(new URL('https://example.com/other'), 'iso1_ctx')
expect(result).to.be.equal(true)
})
it('should allow exact context match', async () => {
store.setSiteAssignments(new Map([['https://exact.example/path', 'iso1_exact']]))
const result = store.isSiteOriginInSameContext(new URL('https://exact.example/another'), 'iso1_exact')
expect(result).to.be.equal(true)
})
it('should block when contexts are not equivalent', async () => {
store.setSiteAssignments(new Map([['https://blocked.example/path', 'container_blocked']]))
await store.setContainerProxyRelation('container_blocked', 'proxy_one')
await store.setContainerProxyRelation('iso1_blocked', 'proxy_two')
const result = store.isSiteOriginInSameContext(new URL('https://blocked.example/another'), 'iso1_blocked')
expect(result).to.be.equal(false)
})
})
})