mirror of
https://github.com/Tencent/BrowserSkill.git
synced 2026-09-10 13:01:23 +08:00
feat(extension): dispatcher fix
This commit is contained in:
@@ -346,20 +346,23 @@ describe("ToolDispatcher", () => {
|
|||||||
};
|
};
|
||||||
const dispatcher = new ToolDispatcher({ transport, sessions, cdp });
|
const dispatcher = new ToolDispatcher({ transport, sessions, cdp });
|
||||||
|
|
||||||
(dispatcher as unknown as { rememberHover: (result: unknown) => unknown }).rememberHover({
|
(
|
||||||
|
dispatcher as unknown as { rememberHover: (sessionId: string, result: unknown) => unknown }
|
||||||
|
).rememberHover("aa11", {
|
||||||
tab_id: 7,
|
tab_id: 7,
|
||||||
x: 10,
|
x: 10,
|
||||||
y: 20,
|
y: 20,
|
||||||
});
|
});
|
||||||
const helpers = dispatcher as unknown as {
|
const helpers = dispatcher as unknown as {
|
||||||
withHoverReassert: <T>(
|
withHoverReassert: <T>(
|
||||||
|
params: { session_id: string; tab_id?: number },
|
||||||
work: () => Promise<T>,
|
work: () => Promise<T>,
|
||||||
options?: { releaseAfter?: boolean },
|
options?: { releaseAfter?: boolean },
|
||||||
) => Promise<T>;
|
) => Promise<T>;
|
||||||
setHoverBypass: (tabId: number, enabled: boolean) => Promise<void>;
|
setHoverBypass: (sessionId: string, tabId: number, enabled: boolean) => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
await helpers.withHoverReassert(async () => {
|
await helpers.withHoverReassert({ session_id: "aa11", tab_id: 7 }, async () => {
|
||||||
order.push("observe");
|
order.push("observe");
|
||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
@@ -369,8 +372,9 @@ describe("ToolDispatcher", () => {
|
|||||||
expect.objectContaining({ enabled: false }),
|
expect.objectContaining({ enabled: false }),
|
||||||
);
|
);
|
||||||
|
|
||||||
await helpers.setHoverBypass(7, true);
|
await helpers.setHoverBypass("aa11", 7, true);
|
||||||
await helpers.withHoverReassert(
|
await helpers.withHoverReassert(
|
||||||
|
{ session_id: "aa11", tab_id: 7 },
|
||||||
async () => {
|
async () => {
|
||||||
order.push("click");
|
order.push("click");
|
||||||
return {};
|
return {};
|
||||||
@@ -389,6 +393,106 @@ describe("ToolDispatcher", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("does not reassert or disable hover latches from another session", async () => {
|
||||||
|
vi.stubGlobal("chrome", {
|
||||||
|
tabs: {
|
||||||
|
sendMessage: vi.fn(async () => undefined),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { transport } = fakeTransport();
|
||||||
|
const sessions = new SessionManager({
|
||||||
|
agentWindow: {
|
||||||
|
create: vi.fn(async () => 1),
|
||||||
|
remove: vi.fn(async () => {}),
|
||||||
|
ensureActiveTab: vi.fn(async () => {}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const cdp = {
|
||||||
|
send: vi.fn(async () => ({})),
|
||||||
|
detachSession: vi.fn(async () => {}),
|
||||||
|
ensureNetworkCapture: vi.fn(async () => {}),
|
||||||
|
networkEntriesSince: vi.fn(() => ({
|
||||||
|
tab_id: 7,
|
||||||
|
entries: [],
|
||||||
|
next_since: 0,
|
||||||
|
truncated: false,
|
||||||
|
})),
|
||||||
|
setDeviceMetricsOverride: vi.fn(async () => {}),
|
||||||
|
clearDeviceMetricsOverride: vi.fn(async () => {}),
|
||||||
|
setUserAgentOverride: vi.fn(async () => {}),
|
||||||
|
setTouchEmulationEnabled: vi.fn(async () => {}),
|
||||||
|
};
|
||||||
|
const dispatcher = new ToolDispatcher({ transport, sessions, cdp });
|
||||||
|
const helpers = dispatcher as unknown as {
|
||||||
|
rememberHover: (sessionId: string, result: unknown) => unknown;
|
||||||
|
setHoverBypass: (sessionId: string, tabId: number, enabled: boolean) => Promise<void>;
|
||||||
|
withHoverReassert: <T>(
|
||||||
|
params: { session_id: string; tab_id?: number },
|
||||||
|
work: () => Promise<T>,
|
||||||
|
options?: { releaseAfter?: boolean },
|
||||||
|
) => Promise<T>;
|
||||||
|
releaseHoverLatch: (sessionId?: string, tabId?: number) => Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
helpers.rememberHover("aa11", { tab_id: 7, x: 10, y: 20 });
|
||||||
|
await helpers.setHoverBypass("aa11", 7, true);
|
||||||
|
|
||||||
|
await helpers.withHoverReassert({ session_id: "bb22", tab_id: 7 }, async () => ({}), {
|
||||||
|
releaseAfter: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(cdp.send).not.toHaveBeenCalled();
|
||||||
|
expect(chrome.tabs.sendMessage).not.toHaveBeenCalledWith(
|
||||||
|
7,
|
||||||
|
expect.objectContaining({ enabled: false }),
|
||||||
|
);
|
||||||
|
|
||||||
|
await helpers.releaseHoverLatch("aa11", 7);
|
||||||
|
expect(chrome.tabs.sendMessage).toHaveBeenCalledWith(
|
||||||
|
7,
|
||||||
|
expect.objectContaining({ enabled: false }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("releases the current session hover latch before navigation-style work", async () => {
|
||||||
|
vi.stubGlobal("chrome", {
|
||||||
|
tabs: {
|
||||||
|
sendMessage: vi.fn(async () => undefined),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { transport } = fakeTransport();
|
||||||
|
const sessions = new SessionManager({
|
||||||
|
agentWindow: {
|
||||||
|
create: vi.fn(async () => 1),
|
||||||
|
remove: vi.fn(async () => {}),
|
||||||
|
ensureActiveTab: vi.fn(async () => {}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const dispatcher = new ToolDispatcher({ transport, sessions });
|
||||||
|
const helpers = dispatcher as unknown as {
|
||||||
|
rememberHover: (sessionId: string, result: unknown) => unknown;
|
||||||
|
setHoverBypass: (sessionId: string, tabId: number, enabled: boolean) => Promise<void>;
|
||||||
|
withHoverReleaseForRequest: <T>(
|
||||||
|
params: { session_id: string; tab_id?: number },
|
||||||
|
work: () => Promise<T>,
|
||||||
|
) => Promise<T>;
|
||||||
|
withHoverReassert: <T>(
|
||||||
|
params: { session_id: string; tab_id?: number },
|
||||||
|
work: () => Promise<T>,
|
||||||
|
) => Promise<T>;
|
||||||
|
};
|
||||||
|
helpers.rememberHover("aa11", { tab_id: 7, x: 10, y: 20 });
|
||||||
|
await helpers.setHoverBypass("aa11", 7, true);
|
||||||
|
|
||||||
|
await helpers.withHoverReleaseForRequest({ session_id: "aa11", tab_id: 7 }, async () => ({}));
|
||||||
|
await helpers.withHoverReassert({ session_id: "aa11", tab_id: 7 }, async () => ({}));
|
||||||
|
|
||||||
|
expect(chrome.tabs.sendMessage).toHaveBeenCalledWith(
|
||||||
|
7,
|
||||||
|
expect.objectContaining({ enabled: false }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("disconnects the transport when send() fails so keepalive can rebuild", async () => {
|
it("disconnects the transport when send() fails so keepalive can rebuild", async () => {
|
||||||
const { transport, deliver } = fakeTransport();
|
const { transport, deliver } = fakeTransport();
|
||||||
const sessions = new SessionManager({
|
const sessions = new SessionManager({
|
||||||
|
|||||||
@@ -83,6 +83,13 @@ type DispatcherCdpRunner = CdpRunner &
|
|||||||
detachSession(sessionId: string): Promise<void>;
|
detachSession(sessionId: string): Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface HoverLatch {
|
||||||
|
sessionId: string;
|
||||||
|
tabId: number;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface DispatcherDeps {
|
export interface DispatcherDeps {
|
||||||
transport: Transport;
|
transport: Transport;
|
||||||
sessions: SessionManager;
|
sessions: SessionManager;
|
||||||
@@ -128,8 +135,8 @@ export class ToolDispatcher {
|
|||||||
private readonly approveBorrow?: BorrowConfirmationApprover;
|
private readonly approveBorrow?: BorrowConfirmationApprover;
|
||||||
private readonly helpNotificationCopy?: () => { title: string; body: string };
|
private readonly helpNotificationCopy?: () => { title: string; body: string };
|
||||||
private subscription: { dispose(): void } | null = null;
|
private subscription: { dispose(): void } | null = null;
|
||||||
private readonly hoverBypassTabs = new Set<number>();
|
private readonly hoverBypassTabs = new Map<number, string>();
|
||||||
private readonly hoverLatches = new Map<number, { x: number; y: number }>();
|
private readonly hoverLatches = new Map<number, HoverLatch>();
|
||||||
/**
|
/**
|
||||||
* Per-rpc-id `AbortController` registry. Populated inside
|
* Per-rpc-id `AbortController` registry. Populated inside
|
||||||
* [`dispatch`] before we await the tool handler and torn down in
|
* [`dispatch`] before we await the tool handler and torn down in
|
||||||
@@ -276,7 +283,7 @@ export class ToolDispatcher {
|
|||||||
case "tool.session_start":
|
case "tool.session_start":
|
||||||
return handleSessionStart(this.sessions, req.params as SessionStartParams);
|
return handleSessionStart(this.sessions, req.params as SessionStartParams);
|
||||||
case "tool.session_stop": {
|
case "tool.session_stop": {
|
||||||
await this.releaseHoverLatch();
|
await this.releaseHoverLatch((req.params as SessionStopParams).session_id);
|
||||||
return handleSessionStop(this.sessions, req.params as SessionStopParams, {
|
return handleSessionStop(this.sessions, req.params as SessionStopParams, {
|
||||||
cdp: this.cdp,
|
cdp: this.cdp,
|
||||||
});
|
});
|
||||||
@@ -286,7 +293,9 @@ export class ToolDispatcher {
|
|||||||
case "tool.tab_create":
|
case "tool.tab_create":
|
||||||
return handleTabCreate(this.sessions, req.params as TabCreateParams);
|
return handleTabCreate(this.sessions, req.params as TabCreateParams);
|
||||||
case "tool.tab_close":
|
case "tool.tab_close":
|
||||||
return handleTabClose(this.sessions, req.params as TabCloseParams);
|
return this.withHoverReleaseForRequest(req.params as TabCloseParams, () =>
|
||||||
|
handleTabClose(this.sessions, req.params as TabCloseParams),
|
||||||
|
);
|
||||||
case "tool.tab_select":
|
case "tool.tab_select":
|
||||||
return handleTabSelect(this.sessions, req.params as TabSelectParams);
|
return handleTabSelect(this.sessions, req.params as TabSelectParams);
|
||||||
case "tool.tab_borrow":
|
case "tool.tab_borrow":
|
||||||
@@ -325,7 +334,7 @@ export class ToolDispatcher {
|
|||||||
this.cdp ? { cdp: this.cdp, tabsApi: chromeTabsApi } : undefined,
|
this.cdp ? { cdp: this.cdp, tabsApi: chromeTabsApi } : undefined,
|
||||||
);
|
);
|
||||||
case "tool.snapshot":
|
case "tool.snapshot":
|
||||||
return this.withHoverReassert(() =>
|
return this.withHoverReassert(req.params as SnapshotParams, () =>
|
||||||
handleSnapshot(
|
handleSnapshot(
|
||||||
this.sessions,
|
this.sessions,
|
||||||
req.params as SnapshotParams,
|
req.params as SnapshotParams,
|
||||||
@@ -333,7 +342,7 @@ export class ToolDispatcher {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
case "tool.observe":
|
case "tool.observe":
|
||||||
return this.withHoverReassert(() =>
|
return this.withHoverReassert(req.params as ObserveParams, () =>
|
||||||
handleObserve(
|
handleObserve(
|
||||||
this.sessions,
|
this.sessions,
|
||||||
req.params as ObserveParams,
|
req.params as ObserveParams,
|
||||||
@@ -341,7 +350,9 @@ export class ToolDispatcher {
|
|||||||
? {
|
? {
|
||||||
cdp: this.cdp,
|
cdp: this.cdp,
|
||||||
tabsApi: chromeTabsCaptureApi,
|
tabsApi: chromeTabsCaptureApi,
|
||||||
conditionalSurfaceProbe: !this.hasHoverLatch(),
|
conditionalSurfaceProbe: !this.hasHoverLatchForRequest(
|
||||||
|
req.params as ObserveParams,
|
||||||
|
),
|
||||||
hoverProbeBypassOverlay: bypassOverlay,
|
hoverProbeBypassOverlay: bypassOverlay,
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
@@ -354,31 +365,40 @@ export class ToolDispatcher {
|
|||||||
this.cdp ? { cdp: this.cdp, tabsApi: chromeTabsCaptureApi } : undefined,
|
this.cdp ? { cdp: this.cdp, tabsApi: chromeTabsCaptureApi } : undefined,
|
||||||
);
|
);
|
||||||
case "tool.navigate":
|
case "tool.navigate":
|
||||||
return handleNavigate(
|
return this.withHoverReleaseForRequest(req.params as NavigateParams, () =>
|
||||||
this.sessions,
|
handleNavigate(
|
||||||
req.params as NavigateParams,
|
this.sessions,
|
||||||
this.cdp ? { cdp: this.cdp, tabsApi: chromeTabsApi, signal } : undefined,
|
req.params as NavigateParams,
|
||||||
|
this.cdp ? { cdp: this.cdp, tabsApi: chromeTabsApi, signal } : undefined,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
case "tool.navigate_back":
|
case "tool.navigate_back":
|
||||||
return handleNavigateBack(
|
return this.withHoverReleaseForRequest(req.params as NavigateBackParams, () =>
|
||||||
this.sessions,
|
handleNavigateBack(
|
||||||
req.params as NavigateBackParams,
|
this.sessions,
|
||||||
this.cdp ? { cdp: this.cdp, tabsApi: chromeTabsApi, signal } : undefined,
|
req.params as NavigateBackParams,
|
||||||
|
this.cdp ? { cdp: this.cdp, tabsApi: chromeTabsApi, signal } : undefined,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
case "tool.navigate_forward":
|
case "tool.navigate_forward":
|
||||||
return handleNavigateForward(
|
return this.withHoverReleaseForRequest(req.params as NavigateForwardParams, () =>
|
||||||
this.sessions,
|
handleNavigateForward(
|
||||||
req.params as NavigateForwardParams,
|
this.sessions,
|
||||||
this.cdp ? { cdp: this.cdp, tabsApi: chromeTabsApi, signal } : undefined,
|
req.params as NavigateForwardParams,
|
||||||
|
this.cdp ? { cdp: this.cdp, tabsApi: chromeTabsApi, signal } : undefined,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
case "tool.reload":
|
case "tool.reload":
|
||||||
return handleReload(
|
return this.withHoverReleaseForRequest(req.params as ReloadParams, () =>
|
||||||
this.sessions,
|
handleReload(
|
||||||
req.params as ReloadParams,
|
this.sessions,
|
||||||
this.cdp ? { cdp: this.cdp, tabsApi: chromeTabsApi, signal } : undefined,
|
req.params as ReloadParams,
|
||||||
|
this.cdp ? { cdp: this.cdp, tabsApi: chromeTabsApi, signal } : undefined,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
case "tool.click":
|
case "tool.click":
|
||||||
return this.withHoverReassert(
|
return this.withHoverReassert(
|
||||||
|
req.params as ClickParams,
|
||||||
() =>
|
() =>
|
||||||
handleClick(
|
handleClick(
|
||||||
this.sessions,
|
this.sessions,
|
||||||
@@ -403,30 +423,37 @@ export class ToolDispatcher {
|
|||||||
cdp: this.cdp,
|
cdp: this.cdp,
|
||||||
tabsApi: chromeTabsApi,
|
tabsApi: chromeTabsApi,
|
||||||
signal,
|
signal,
|
||||||
bypassOverlay: (tabId, enabled) => this.setHoverBypass(tabId, enabled),
|
bypassOverlay: (tabId, enabled) =>
|
||||||
|
this.setHoverBypass((req.params as HoverParams).session_id, tabId, enabled),
|
||||||
keepOverlayBypassAfterHover: true,
|
keepOverlayBypassAfterHover: true,
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
);
|
);
|
||||||
return this.rememberHover(result);
|
return this.rememberHover((req.params as HoverParams).session_id, result);
|
||||||
}
|
}
|
||||||
case "tool.fill":
|
case "tool.fill":
|
||||||
return handleFill(
|
return this.withHoverReleaseForRequest(req.params as FillParams, () =>
|
||||||
this.sessions,
|
handleFill(
|
||||||
req.params as FillParams,
|
this.sessions,
|
||||||
this.cdp ? { cdp: this.cdp, tabsApi: chromeTabsApi, signal } : undefined,
|
req.params as FillParams,
|
||||||
|
this.cdp ? { cdp: this.cdp, tabsApi: chromeTabsApi, signal } : undefined,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
case "tool.press":
|
case "tool.press":
|
||||||
return handlePress(
|
return this.withHoverReleaseForRequest(req.params as PressParams, () =>
|
||||||
this.sessions,
|
handlePress(
|
||||||
req.params as PressParams,
|
this.sessions,
|
||||||
this.cdp ? { cdp: this.cdp, tabsApi: chromeTabsApi, signal } : undefined,
|
req.params as PressParams,
|
||||||
|
this.cdp ? { cdp: this.cdp, tabsApi: chromeTabsApi, signal } : undefined,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
case "tool.select":
|
case "tool.select":
|
||||||
return handleSelect(
|
return this.withHoverReleaseForRequest(req.params as SelectParams, () =>
|
||||||
this.sessions,
|
handleSelect(
|
||||||
req.params as SelectParams,
|
this.sessions,
|
||||||
this.cdp ? { cdp: this.cdp, tabsApi: chromeTabsApi, signal } : undefined,
|
req.params as SelectParams,
|
||||||
|
this.cdp ? { cdp: this.cdp, tabsApi: chromeTabsApi, signal } : undefined,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
case "tool.evaluate":
|
case "tool.evaluate":
|
||||||
return handleEvaluate(
|
return handleEvaluate(
|
||||||
@@ -500,11 +527,11 @@ export class ToolDispatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async setHoverBypass(tabId: number, enabled: boolean): Promise<void> {
|
private async setHoverBypass(sessionId: string, tabId: number, enabled: boolean): Promise<void> {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
if (this.hoverBypassTabs.has(tabId)) return;
|
if (this.hoverBypassTabs.has(tabId)) return;
|
||||||
await bypassOverlay(tabId, true);
|
await bypassOverlay(tabId, true);
|
||||||
this.hoverBypassTabs.add(tabId);
|
this.hoverBypassTabs.set(tabId, sessionId);
|
||||||
} else {
|
} else {
|
||||||
if (!this.hoverBypassTabs.has(tabId)) return;
|
if (!this.hoverBypassTabs.has(tabId)) return;
|
||||||
await bypassOverlay(tabId, false);
|
await bypassOverlay(tabId, false);
|
||||||
@@ -512,51 +539,84 @@ export class ToolDispatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private rememberHover(result: HoverResult | RpcError): HoverResult | RpcError {
|
private rememberHover(sessionId: string, result: HoverResult | RpcError): HoverResult | RpcError {
|
||||||
if (!isRpcError(result)) {
|
if (!isRpcError(result)) {
|
||||||
this.hoverLatches.set(result.tab_id, { x: result.x, y: result.y });
|
this.hoverLatches.set(result.tab_id, {
|
||||||
|
sessionId,
|
||||||
|
tabId: result.tab_id,
|
||||||
|
x: result.x,
|
||||||
|
y: result.y,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private hasHoverLatch(): boolean {
|
private hasHoverLatchForRequest(params: { session_id: string; tab_id?: number }): boolean {
|
||||||
return this.hoverLatches.size > 0;
|
return this.hoverLatchesForRequest(params).length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async withHoverReassert<T>(
|
private async withHoverReassert<T>(
|
||||||
|
params: { session_id: string; tab_id?: number },
|
||||||
work: () => Promise<T>,
|
work: () => Promise<T>,
|
||||||
options: { releaseAfter?: boolean } = {},
|
options: { releaseAfter?: boolean } = {},
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
await this.reassertHover();
|
await this.reassertHover(params);
|
||||||
try {
|
try {
|
||||||
return await work();
|
return await work();
|
||||||
} finally {
|
} finally {
|
||||||
if (options.releaseAfter) {
|
if (options.releaseAfter) {
|
||||||
await this.releaseHoverLatch();
|
await this.releaseHoverLatch(params.session_id, params.tab_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async reassertHover(): Promise<void> {
|
private async withHoverReleaseForRequest<T>(
|
||||||
|
params: { session_id: string; tab_id?: number },
|
||||||
|
work: () => Promise<T>,
|
||||||
|
): Promise<T> {
|
||||||
|
await this.releaseHoverLatch(params.session_id, params.tab_id);
|
||||||
|
return work();
|
||||||
|
}
|
||||||
|
|
||||||
|
private hoverLatchesForRequest(params: { session_id: string; tab_id?: number }): HoverLatch[] {
|
||||||
|
return [...this.hoverLatches.values()].filter((latch) => {
|
||||||
|
if (latch.sessionId !== params.session_id) return false;
|
||||||
|
return params.tab_id === undefined || latch.tabId === params.tab_id;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async reassertHover(params: { session_id: string; tab_id?: number }): Promise<void> {
|
||||||
if (!this.cdp) return;
|
if (!this.cdp) return;
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
[...this.hoverLatches.entries()].map(([tabId, point]) =>
|
this.hoverLatchesForRequest(params).map((latch) =>
|
||||||
this.cdp!.send(tabId, "Input.dispatchMouseEvent", {
|
this.cdp!.send(latch.tabId, "Input.dispatchMouseEvent", {
|
||||||
type: "mouseMoved",
|
type: "mouseMoved",
|
||||||
x: point.x,
|
x: latch.x,
|
||||||
y: point.y,
|
y: latch.y,
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.debug("[bsk dispatcher] hover reassert failed", err);
|
console.debug("[bsk dispatcher] hover reassert failed", err);
|
||||||
this.hoverLatches.delete(tabId);
|
this.hoverLatches.delete(latch.tabId);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async releaseHoverLatch(): Promise<void> {
|
private async releaseHoverLatch(sessionId?: string, tabId?: number): Promise<void> {
|
||||||
const tabs = new Set([...this.hoverBypassTabs, ...this.hoverLatches.keys()]);
|
const matchesScope = (entrySessionId: string, entryTabId: number): boolean => {
|
||||||
this.hoverBypassTabs.clear();
|
if (sessionId !== undefined && entrySessionId !== sessionId) return false;
|
||||||
this.hoverLatches.clear();
|
return tabId === undefined || entryTabId === tabId;
|
||||||
|
};
|
||||||
|
const tabs = new Set<number>();
|
||||||
|
for (const [bypassTabId, bypassSessionId] of this.hoverBypassTabs) {
|
||||||
|
if (!matchesScope(bypassSessionId, bypassTabId)) continue;
|
||||||
|
tabs.add(bypassTabId);
|
||||||
|
this.hoverBypassTabs.delete(bypassTabId);
|
||||||
|
}
|
||||||
|
for (const latch of this.hoverLatches.values()) {
|
||||||
|
if (!matchesScope(latch.sessionId, latch.tabId)) continue;
|
||||||
|
tabs.add(latch.tabId);
|
||||||
|
this.hoverLatches.delete(latch.tabId);
|
||||||
|
}
|
||||||
await Promise.all([...tabs].map((tabId) => bypassOverlay(tabId, false)));
|
await Promise.all([...tabs].map((tabId) => bypassOverlay(tabId, false)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user