mirror of
https://github.com/stablyai/orca.git
synced 2026-11-14 01:47:24 +00:00
refactor(window): organize clipboard, lifecycle, security, and relay owners (#41634)
* refactor(window): group clipboard, lifecycle, security, and relay owners * refactor(window): keep Electron paste handling with desktop clipboard
This commit is contained in:
@@ -12,7 +12,7 @@ on:
|
||||
- 'config/scripts/macos-shipit-scheduling*'
|
||||
- 'config/electron-builder.config.cjs'
|
||||
- 'src/main/startup/quit/main-process-quit*'
|
||||
- 'src/main/window/main-window-state-lifecycle*'
|
||||
- 'src/main/window/main-window/main-window-state-lifecycle*'
|
||||
- 'src/main/window/dashboard-popout-window*'
|
||||
- 'src/packages/process-host/**'
|
||||
- 'src/main/runtime/agent-session/lifecycle/structured-agent-cli-writers*'
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
src/main/updater/updater.headless-serve-install.test.ts
|
||||
src/main/updater/mac-install/updater-mac-quit-guard.test.ts
|
||||
src/main/startup/quit/main-process-quit-update-veto.test.ts
|
||||
src/main/window/main-window-state-lifecycle.test.ts
|
||||
src/main/window/main-window/main-window-state-lifecycle.test.ts
|
||||
- name: Prove login-backed terminal writer census on the disposable runner
|
||||
env:
|
||||
ORCA_MACOS_LOGIN_CENSUS_CI: '1'
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
1 test-no-in-test-import-actual:src/main/speech/openai-api-key-store.test.ts
|
||||
5 test-no-in-test-import-actual:src/main/ssh/config/ssh-config-loader-regression.test.ts
|
||||
2 test-no-in-test-import-actual:src/main/ssh/connection/ssh-connection-auth-fallback.test.ts
|
||||
1 test-no-in-test-import-actual:src/main/window/dropped-path-preparation.test.ts
|
||||
1 test-no-in-test-import-actual:src/main/window/file-drop/dropped-path-preparation.test.ts
|
||||
2 test-no-in-test-import-actual:src/main/workspace-hooks/hooks-runner.test.ts
|
||||
1 test-no-in-test-import-actual:src/main/workspaces/removal/local-worktree-removal-recovery.test.ts
|
||||
1 test-no-in-test-import-actual:src/renderer/src/components/terminal-pane/pty-connection/notifications/pty-connection-hook-idle-arbitration.test.ts
|
||||
@@ -201,7 +201,7 @@
|
||||
2 test-no-real-time-wait:src/main/startup/gpu-recovery/gpu-lifecycle-install-dir-acl-guard.test.ts
|
||||
3 test-no-real-time-wait:config/scripts/development/launch/run-electron-vite-dev.test.ts
|
||||
1 test-no-real-time-wait:src/main/updater/lifecycle/serve-supervisor-install-park.test.ts
|
||||
1 test-no-real-time-wait:src/main/window/main-window-updater-deferred-setup.test.ts
|
||||
1 test-no-real-time-wait:src/main/updater/lifecycle/main-window-updater-deferred-setup.test.ts
|
||||
3 test-no-real-time-wait:src/main/windows/windows-host-job.win32.test.ts
|
||||
1 test-no-real-time-wait:src/main/windows/windows-process-tree-command-line-patch.test.ts
|
||||
2 test-no-real-time-wait:src/main/windows/windows-pty-job.win32.test.ts
|
||||
@@ -630,7 +630,7 @@
|
||||
1 test-timeout-needs-reason:src/main/sqlite/sync-database-concurrent-backup.test.ts
|
||||
2 test-timeout-needs-reason:src/main/ssh/connection/ssh-connection-sftp-wire.test.ts
|
||||
2 test-timeout-needs-reason:src/main/ssh/system-transport/forwarding/system-ssh-forward-process-real-openssh.test.ts
|
||||
1 test-timeout-needs-reason:src/main/window/renderer-publication-throttle.electron.test.ts
|
||||
1 test-timeout-needs-reason:src/main/window/visibility/renderer-publication-throttle.electron.test.ts
|
||||
1 test-timeout-needs-reason:src/main/windows/windows-administrator-restart.win32.test.ts
|
||||
1 test-timeout-needs-reason:src/main/windows/windows-host-job.win32.test.ts
|
||||
1 test-timeout-needs-reason:src/main/windows/windows-msys-job.win32.test.ts
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -113,13 +113,13 @@ const NODE_RUNTIME_INCLUDE = [
|
||||
'src/shared/workspace-session/workspace-session-salvage.test.ts',
|
||||
'src/main/git/repositories/discovery/repo-detection.test.ts',
|
||||
'src/renderer/src/components/browser-pane/annotate/browser-grab-toast-owner-cleanup.test.tsx',
|
||||
'src/main/window/native-chat-paste-files.test.ts',
|
||||
'src/main/native-chat/paste-files/native-chat-paste-files.test.ts',
|
||||
'src/main/computer/desktop-script/runtime-host/desktop-script-serve-channel.test.ts',
|
||||
'src/main/providers/local-pty/local-pty-child-process-verdict.test.ts',
|
||||
'src/main/providers/working-directory-validation.test.ts',
|
||||
'src/main/pty/process-retirement/posix-pty-process-groups.integration.test.ts',
|
||||
'src/shared/ephemeral-vm-recipe-output-capture.test.ts',
|
||||
'src/main/window/main-window-updater-deferred-setup.test.ts',
|
||||
'src/main/updater/lifecycle/main-window-updater-deferred-setup.test.ts',
|
||||
'src/renderer/src/components/terminal-pane/host-state/terminal-pane-host-state-memo.test.ts',
|
||||
'src/main/native-chat/agent-session-journal/**/*.test.ts',
|
||||
'src/main/native-chat/agent-session-wire/host/testing/structured-agent-session-terminal-writer-acquire.test.ts',
|
||||
|
||||
@@ -151,7 +151,7 @@ export const PR_E2E_SOURCE_ROUTES = [
|
||||
specs: ['tests/e2e/file-explorer-copy-file-windows.spec.ts'],
|
||||
matches: (file) =>
|
||||
isProductSource(file) &&
|
||||
/^src\/(?:main\/window\/clipboard-(?:file-copy|ipc-handlers)|renderer\/src\/components\/right-sidebar\/file-explorer\/panel\/file-explorer-row-file-transfer)\.ts$/.test(
|
||||
/^src\/(?:main\/window\/clipboard\/clipboard-(?:file-copy|ipc-handlers)|renderer\/src\/components\/right-sidebar\/file-explorer\/panel\/file-explorer-row-file-transfer)\.ts$/.test(
|
||||
file
|
||||
)
|
||||
},
|
||||
|
||||
@@ -123,7 +123,7 @@ const SPECIFIC_PLUGIN_ROUTES = [
|
||||
'src\\/main\\/plugins\\/panels\\/(?:frame-serving\\/|guest-isolation\\/|desktop-actions\\/)?plugin-frame-',
|
||||
'src\\/shared\\/plugins\\/frames\\/plugin-(?:frame-guest|panel-shell)',
|
||||
'src\\/preload\\/plugin-frame-guest',
|
||||
'src\\/main\\/window\\/(?:main-window-webview-security|attach-main-window-services|host-frame-permission-policy)',
|
||||
'src\\/main\\/window\\/(?:security/(?:main-window-webview-security|host-frame-permission-policy)|attach-main-window-services)',
|
||||
'src\\/main\\/ipc\\/(?:plugins|plugin-frame-entry-ipc)\\.ts$',
|
||||
'src\\/main\\/startup\\/plugin-startup\\/main-process-plugins',
|
||||
'config\\/build-plugins\\/main-build-entry-points',
|
||||
|
||||
@@ -92,9 +92,9 @@
|
||||
"../src/main/startup/serve-launch/serve-mode-argv.ts",
|
||||
"../src/main/startup/profile-startup/single-instance-lock.ts",
|
||||
"../src/main/startup/diagnostics/startup-diagnostics.ts",
|
||||
"../src/main/window/focus-existing-window.ts",
|
||||
"../src/main/window/foreground-activation-policy.ts",
|
||||
"../src/main/window/macos-app-activation.ts"
|
||||
"../src/main/window/activation/focus-existing-window.ts",
|
||||
"../src/main/window/activation/foreground-activation-policy.ts",
|
||||
"../src/main/window/activation/macos-app-activation.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { loadKnownUsageWorktreesByRepo } from '../usage/worktree-attribution/usa
|
||||
import { isTelemetryEnabled, track } from '../telemetry/client'
|
||||
import type { UsageWorktreeSource } from '../usage/worktree-attribution/usage-worktree-metadata'
|
||||
import { currentWorkerEntryLayout } from '../worker-threads/worker-thread-entry-path'
|
||||
import { isRendererDocumentNavigation } from '../window/renderer-document-navigation'
|
||||
import { isRendererDocumentNavigation } from '../window/security/renderer-document-navigation'
|
||||
|
||||
export function trustsBackendRenderer(contents: WebContents): boolean {
|
||||
if (
|
||||
|
||||
@@ -37,7 +37,7 @@ vi.mock('../worker-threads/worker-thread-entry-path', () => ({
|
||||
currentWorkerEntryLayout: () => ({ moduleDir: '/' }),
|
||||
resolveWorkerThreadEntryPath: () => 'backend-entry.js'
|
||||
}))
|
||||
vi.mock('../window/renderer-document-navigation', () => ({
|
||||
vi.mock('../window/security/renderer-document-navigation', () => ({
|
||||
isRendererDocumentNavigation: () => true
|
||||
}))
|
||||
vi.mock('../usage/worktree-attribution/usage-worktree-metadata', () => ({
|
||||
|
||||
@@ -2,7 +2,7 @@ import { app, BaseWindow, BrowserWindow, protocol, ipcMain, session } from 'elec
|
||||
import { renameSync, writeFileSync } from 'node:fs'
|
||||
import '../../../../shared/constants'
|
||||
import { ORCA_BROWSER_PARTITION } from '../../../../shared/browser/browser-session-partition'
|
||||
import { applyBackgroundActivationPolicy } from '../../../window/foreground-activation-policy'
|
||||
import { applyBackgroundActivationPolicy } from '../../../window/activation/foreground-activation-policy'
|
||||
import { browserManager } from '../../guest-lifecycle/manager/browser-manager'
|
||||
import { browserSessionRegistry } from '../../sessions/profiles/browser-session-registry'
|
||||
import { initializeBrowserExtensionSupport } from '../browser-extension-startup'
|
||||
|
||||
@@ -3,7 +3,7 @@ import { renameSync, writeFileSync } from 'node:fs'
|
||||
import { createServer } from 'node:http'
|
||||
import '../../../../shared/constants'
|
||||
import { ORCA_BROWSER_PARTITION } from '../../../../shared/browser/browser-session-partition'
|
||||
import { applyBackgroundActivationPolicy } from '../../../window/foreground-activation-policy'
|
||||
import { applyBackgroundActivationPolicy } from '../../../window/activation/foreground-activation-policy'
|
||||
import { browserManager } from '../../guest-lifecycle/manager/browser-manager'
|
||||
import { browserSessionRegistry } from '../../sessions/profiles/browser-session-registry'
|
||||
import { initializeBrowserExtensionSupport } from '../browser-extension-startup'
|
||||
|
||||
@@ -27,7 +27,7 @@ vi.mock('electron', async () => {
|
||||
nativeImage: {}
|
||||
}
|
||||
})
|
||||
vi.mock('../../../window/foreground-activation-policy', () => ({
|
||||
vi.mock('../../../window/activation/foreground-activation-policy', () => ({
|
||||
isBackgroundLaunch: () => mocks.background
|
||||
}))
|
||||
vi.mock('../api-runtime/browser-extension-api-host', () => ({ emitBrowserExtensionEvent: vi.fn() }))
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
getChromeExtensionOrigin,
|
||||
parseChromeExtensionUrl
|
||||
} from '../../../../shared/browser/extensions/browser-extension-url'
|
||||
import { isBackgroundLaunch } from '../../../window/foreground-activation-policy'
|
||||
import { isBackgroundLaunch } from '../../../window/activation/foreground-activation-policy'
|
||||
import { emitBrowserExtensionEvent } from '../api-runtime/browser-extension-api-host'
|
||||
import { resolveInsideExtensionDirectory } from '../installation/browser-extension-manifest'
|
||||
import {
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import { existsSync, mkdirSync, renameSync, writeFileSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import '../../../../shared/constants'
|
||||
import { ORCA_BROWSER_PARTITION } from '../../../../shared/browser/browser-session-partition'
|
||||
import { applyBackgroundActivationPolicy } from '../../../window/foreground-activation-policy'
|
||||
import { applyBackgroundActivationPolicy } from '../../../window/activation/foreground-activation-policy'
|
||||
import { browserSessionRegistry } from '../../sessions/profiles/browser-session-registry'
|
||||
import { initializeBrowserExtensionSupport } from '../browser-extension-startup'
|
||||
import { BROWSER_EXTENSION_API_CUSTOM_SCHEME } from '../api-runtime/browser-extension-api-scheme'
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { app, BaseWindow, protocol, session } from 'electron'
|
||||
import { renameSync, writeFileSync } from 'node:fs'
|
||||
import '../../../../shared/constants'
|
||||
import { ORCA_BROWSER_PARTITION } from '../../../../shared/browser/browser-session-partition'
|
||||
import { applyBackgroundActivationPolicy } from '../../../window/foreground-activation-policy'
|
||||
import { applyBackgroundActivationPolicy } from '../../../window/activation/foreground-activation-policy'
|
||||
import { browserSessionRegistry } from '../../sessions/profiles/browser-session-registry'
|
||||
import { initializeBrowserExtensionSupport } from '../browser-extension-startup'
|
||||
import { BROWSER_EXTENSION_API_CUSTOM_SCHEME } from '../api-runtime/browser-extension-api-scheme'
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import { renameSync, writeFileSync } from 'node:fs'
|
||||
import { createServer } from 'node:http'
|
||||
import '../../../../shared/constants'
|
||||
import { ORCA_BROWSER_PARTITION } from '../../../../shared/browser/browser-session-partition'
|
||||
import { applyBackgroundActivationPolicy } from '../../../window/foreground-activation-policy'
|
||||
import { applyBackgroundActivationPolicy } from '../../../window/activation/foreground-activation-policy'
|
||||
import { browserManager } from '../../guest-lifecycle/manager/browser-manager'
|
||||
import { browserSessionRegistry } from '../../sessions/profiles/browser-session-registry'
|
||||
import { initializeBrowserExtensionSupport } from '../browser-extension-startup'
|
||||
|
||||
@@ -26,7 +26,7 @@ vi.mock('electron', async () => {
|
||||
}
|
||||
return { BrowserWindow: FakeBrowserWindow }
|
||||
})
|
||||
vi.mock('../../../window/foreground-activation-policy', () => ({
|
||||
vi.mock('../../../window/activation/foreground-activation-policy', () => ({
|
||||
isBackgroundLaunch: () => true,
|
||||
showWindowWithoutStealingFocus: () => {}
|
||||
}))
|
||||
|
||||
@@ -5,7 +5,7 @@ import { isSameExtensionNavigation } from '../../../../shared/browser/extensions
|
||||
import {
|
||||
isBackgroundLaunch,
|
||||
showWindowWithoutStealingFocus
|
||||
} from '../../../window/foreground-activation-policy'
|
||||
} from '../../../window/activation/foreground-activation-policy'
|
||||
import { emitBrowserExtensionEvent } from '../api-runtime/browser-extension-api-host'
|
||||
import { watchBrowserMainWindowReadiness } from '../../guest-lifecycle/browser-main-window-readiness'
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { app, BrowserWindow } from 'electron'
|
||||
import { applyBackgroundActivationPolicy } from '../../../window/foreground-activation-policy'
|
||||
import { applyBackgroundActivationPolicy } from '../../../window/activation/foreground-activation-policy'
|
||||
|
||||
if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') {
|
||||
throw new Error('Find traversal fixture requires background launch')
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
redactKagiSessionToken
|
||||
} from '../../../../shared/browser-url'
|
||||
import type { BrowserContextMenuRequestedEvent } from '../../../../shared/browser/guest-lifecycle/browser-guest-events'
|
||||
import { readContextMenuSpelling } from '../../../window/editable-context-menu'
|
||||
import { readContextMenuSpelling } from '../../../window/editing/editable-context-menu'
|
||||
import { readGuestNavigationState } from '../navigation/browser-guest-navigation-state'
|
||||
import type { ResolveRenderer } from '../browser-guest-renderer-target'
|
||||
// oxlint-disable-next-line import/no-cycle -- browser subsystem singleton hub (browserManager); breaking it needs DI and test-mock updates, left for a human
|
||||
|
||||
@@ -40,7 +40,7 @@ vi.mock('../popups/presentation/popup-origin-bar-window', () => ({
|
||||
}))
|
||||
|
||||
import { browserManager } from './browser-manager'
|
||||
import { rendererPublicationThrottle } from '../../../window/renderer-publication-throttle'
|
||||
|
||||
import {
|
||||
guestNavigationMethods,
|
||||
rendererWebContentsId,
|
||||
@@ -60,28 +60,6 @@ describe('browserManager', () => {
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
it('shares guest leases with a concurrent stream even without a desktop renderer', () => {
|
||||
vi.useFakeTimers()
|
||||
const guest = {
|
||||
id: 1708,
|
||||
isDestroyed: vi.fn(() => false),
|
||||
setBackgroundThrottling: vi.fn(),
|
||||
capturePage: vi.fn(async () => null)
|
||||
}
|
||||
webContentsFromIdMock.mockReturnValue(guest)
|
||||
const releaseStream = rendererPublicationThrottle.acquire(guest)
|
||||
const releaseCapture = browserManager.holdPaintForCapture(guest.id)
|
||||
|
||||
releaseStream()
|
||||
vi.runAllTimers()
|
||||
expect(guest.setBackgroundThrottling.mock.calls).toEqual([[false]])
|
||||
|
||||
releaseCapture()
|
||||
releaseCapture()
|
||||
vi.runAllTimers()
|
||||
expect(guest.setBackgroundThrottling.mock.calls).toEqual([[false], [true]])
|
||||
})
|
||||
|
||||
it.each<string>(['acquire'])('cleans capture ownership after renderer %s fails', (failure) => {
|
||||
vi.useFakeTimers()
|
||||
const guest = {
|
||||
@@ -139,70 +117,4 @@ describe('browserManager', () => {
|
||||
vi.runAllTimers()
|
||||
expect(guest.setBackgroundThrottling.mock.calls).toEqual([[false], [true], [false], [true]])
|
||||
})
|
||||
|
||||
it('does not let a destroyed guest and renderer release a replacement page hold', () => {
|
||||
vi.useFakeTimers()
|
||||
const makeGuest = (id: number) => ({
|
||||
...guestNavigationMethods(),
|
||||
id,
|
||||
isDestroyed: vi.fn(() => false),
|
||||
isCurrentlyAudible: vi.fn(() => false),
|
||||
getType: vi.fn(() => 'webview'),
|
||||
setBackgroundThrottling: vi.fn(),
|
||||
capturePage: vi.fn(async () => null),
|
||||
setWindowOpenHandler: vi.fn(),
|
||||
on: vi.fn(),
|
||||
off: vi.fn(),
|
||||
openDevTools: vi.fn()
|
||||
})
|
||||
const makeRenderer = (id: number) => ({
|
||||
id,
|
||||
isDestroyed: vi.fn(() => false),
|
||||
send: vi.fn(),
|
||||
setBackgroundThrottling: vi.fn(),
|
||||
capturePage: vi.fn(async () => null)
|
||||
})
|
||||
const oldGuest = makeGuest(1709)
|
||||
const newGuest = makeGuest(1710)
|
||||
const oldRenderer = makeRenderer(rendererWebContentsId)
|
||||
const newRenderer = makeRenderer(rendererWebContentsId + 1)
|
||||
webContentsFromIdMock.mockImplementation((id: number) =>
|
||||
[oldGuest, newGuest, oldRenderer, newRenderer].find((contents) => contents.id === id)
|
||||
)
|
||||
const register = (guest: ReturnType<typeof makeGuest>, rendererId: number) => {
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: this guest provides the policy and registration members exercised here.
|
||||
browserManager.attachGuestPolicies(guest as never)
|
||||
browserManager.registerGuest({
|
||||
browserPageId: 'page-replaced',
|
||||
webContentsId: guest.id,
|
||||
rendererWebContentsId: rendererId
|
||||
})
|
||||
}
|
||||
register(oldGuest, oldRenderer.id)
|
||||
const releaseOld = browserManager.holdPaintForCapture(oldGuest.id)
|
||||
oldGuest.isDestroyed.mockReturnValue(true)
|
||||
oldRenderer.isDestroyed.mockReturnValue(true)
|
||||
register(newGuest, newRenderer.id)
|
||||
newRenderer.send.mockClear()
|
||||
const releaseNew = browserManager.holdPaintForCapture(newGuest.id)
|
||||
|
||||
releaseOld()
|
||||
vi.runAllTimers()
|
||||
expect(newRenderer.send.mock.calls).toEqual([
|
||||
['browser:capturePaintHold', { browserPageId: 'page-replaced', held: true }]
|
||||
])
|
||||
expect(oldGuest.setBackgroundThrottling.mock.calls).toEqual([[false]])
|
||||
expect(oldRenderer.setBackgroundThrottling.mock.calls).toEqual([[false]])
|
||||
expect(newGuest.setBackgroundThrottling.mock.calls).toEqual([[false]])
|
||||
expect(newRenderer.setBackgroundThrottling.mock.calls).toEqual([[false]])
|
||||
|
||||
releaseNew()
|
||||
vi.runAllTimers()
|
||||
expect(newRenderer.send.mock.calls.at(-1)).toEqual([
|
||||
'browser:capturePaintHold',
|
||||
{ browserPageId: 'page-replaced', held: false }
|
||||
])
|
||||
expect(newGuest.setBackgroundThrottling.mock.calls).toEqual([[false], [true]])
|
||||
expect(newRenderer.setBackgroundThrottling.mock.calls).toEqual([[false], [true]])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { webContents } from 'electron'
|
||||
import { rendererPublicationThrottle } from '../../../window/renderer-publication-throttle'
|
||||
import { rendererPublicationThrottle } from '../../../window/visibility/renderer-publication-throttle'
|
||||
import { BrowserManagerState } from './browser-manager-state'
|
||||
import {
|
||||
focusGuardUnavailableError,
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { app, BrowserWindow, ipcMain, session, webContents } from 'electron'
|
||||
import { join } from 'node:path'
|
||||
import { applyBackgroundActivationPolicy } from '../../../window/foreground-activation-policy'
|
||||
import { applyBackgroundActivationPolicy } from '../../../window/activation/foreground-activation-policy'
|
||||
|
||||
if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') {
|
||||
throw new Error('Popup identity probes require a background launch')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BrowserWindow, ipcMain, type WebContents } from 'electron'
|
||||
import { isResourceWindowVisible } from '../../../window/resource-view-admission'
|
||||
import { isResourceWindowVisible } from '../../../window/visibility/resource-view-admission'
|
||||
import { sendGuestCdpCommand } from '../../cdp/debugger/guest-cdp-command'
|
||||
import { isAgentRpcCaller } from '../../../runtime/rpc/callers/rpc-caller-identity'
|
||||
import {
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ import {
|
||||
} from 'electron'
|
||||
import { writeFileSync } from 'node:fs'
|
||||
import { renameFileWithWindowsRetry } from '../../../../../shared/windows-retry-file-operations'
|
||||
import { applyBackgroundActivationPolicy } from '../../../../window/foreground-activation-policy'
|
||||
import { applyBackgroundActivationPolicy } from '../../../../window/activation/foreground-activation-policy'
|
||||
import { BrowserPopupCompatibilityManager } from './browser-popup-compatibility-manager-fixture'
|
||||
import { BrowserRouteWebContentsRegistry } from '../../../routing/guest-authority/browser-route-webcontents-registry'
|
||||
import { initializeBrowserProcessUserAgent } from '../../../identity/browser-process-user-agent'
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { BrowserWindow, WebContents } from 'electron'
|
||||
import {
|
||||
isBackgroundLaunch,
|
||||
showWhenAppActive
|
||||
} from '../../../../window/foreground-activation-policy'
|
||||
} from '../../../../window/activation/foreground-activation-policy'
|
||||
import type { PopupChildWindowOptions, PopupOriginBarWindow } from './browser-popup-window-contract'
|
||||
import {
|
||||
isNativeBrowserPopupWindow,
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
isBackgroundLaunch,
|
||||
revealWindowsAwaitingAppActive,
|
||||
showWhenAppActive
|
||||
} from '../../../../window/foreground-activation-policy'
|
||||
} from '../../../../window/activation/foreground-activation-policy'
|
||||
import {
|
||||
discardBrowserPopupContents,
|
||||
prepareNativeBrowserPopup
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import { mkdirSync, writeFileSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import { openPopupWithOriginBar } from './popup-origin-bar-window'
|
||||
import { prepareNativeBrowserPopup } from './browser-popup-native-window'
|
||||
import { applyBackgroundActivationPolicy } from '../../../../window/foreground-activation-policy'
|
||||
import { applyBackgroundActivationPolicy } from '../../../../window/activation/foreground-activation-policy'
|
||||
|
||||
if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') {
|
||||
throw new Error('Popup identity probes require a background launch')
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import type { Dir } from 'node:fs'
|
||||
import { isSafeOwnedDirectory } from '../../shared/owned-private-directory'
|
||||
export { isSafeOwnedDirectory } from '../../shared/owned-private-directory'
|
||||
|
||||
import { lstat, mkdir, opendir } from 'node:fs/promises'
|
||||
import { dirname, join, resolve } from 'node:path'
|
||||
import { removeTree } from '../../shared/windows-transient-lock-removal'
|
||||
@@ -1,4 +1,4 @@
|
||||
import { isResourceWindowVisible } from '../window/resource-view-admission'
|
||||
import { isResourceWindowVisible } from '../window/visibility/resource-view-admission'
|
||||
import { BrowserWindow, ipcMain, Notification, powerMonitor } from 'electron'
|
||||
import { readDesktopAwayState } from '../notifications/desktop-away-state'
|
||||
import type { RuntimeDesktopSurface } from '../runtime/construction/runtime-desktop-surface'
|
||||
|
||||
+9
-3
@@ -1,8 +1,14 @@
|
||||
import { app, clipboard, dialog, type BrowserWindow } from 'electron'
|
||||
import { isRoutedOrcaDeepLink } from './startup/open-routing/orca-deep-link-url'
|
||||
import { createMacAppActivationHandler } from './window/macos-app-activation'
|
||||
import { isBackgroundLaunch, isOrcadSidecarLaunch } from './window/foreground-activation-policy'
|
||||
import { createDesktopHandoff, desktopHandoffArguments } from './window/macos-desktop-handoff'
|
||||
import { createMacAppActivationHandler } from './window/activation/macos-app-activation'
|
||||
import {
|
||||
isBackgroundLaunch,
|
||||
isOrcadSidecarLaunch
|
||||
} from './window/activation/foreground-activation-policy'
|
||||
import {
|
||||
createDesktopHandoff,
|
||||
desktopHandoffArguments
|
||||
} from './window/activation/macos-desktop-handoff'
|
||||
import {
|
||||
focusExistingWindow as focusExistingWindowAction,
|
||||
setMainWindowOpener
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ import type { AppIdentity } from '../../shared/app-identity'
|
||||
import type { MarkdownDocument } from '../../shared/file-listing/file-document'
|
||||
import type { FloatingTerminalCwdRequest } from '../../shared/ui-chrome-types'
|
||||
import { relaunchApp, runBeforeRelaunchCleanup } from '../startup/quit/app-relaunch'
|
||||
import { setWindowMaterialSuppressed } from '../window/window-material-suppression'
|
||||
import { setWindowMaterialSuppressed } from '../window/presentation/window-material-suppression'
|
||||
import { isTrustedUIRenderer } from './ui'
|
||||
import { prepareWindowsAdministratorRestart } from '../windows/windows-administrator-restart'
|
||||
import { quitProcess } from '../startup/quit/process-quit-request'
|
||||
|
||||
@@ -9,8 +9,8 @@ import {
|
||||
isDashboardPopoutRenderer,
|
||||
onDashboardPopoutOpenChanged
|
||||
} from '../window/dashboard-popout-window'
|
||||
import { safelyRevealWindow } from '../window/focus-existing-window'
|
||||
import { isBackgroundLaunch } from '../window/foreground-activation-policy'
|
||||
import { safelyRevealWindow } from '../window/activation/focus-existing-window'
|
||||
import { isBackgroundLaunch } from '../window/activation/foreground-activation-policy'
|
||||
import { getTrustedUIRendererWindow, isTrustedUIRenderer, sendToTrustedUIRenderer } from './ui'
|
||||
import {
|
||||
admitDashboardSnapshot,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { collectMemorySnapshot } from '../memory/collector'
|
||||
import {
|
||||
assertResourceViewVisible,
|
||||
isResourceWindowVisible
|
||||
} from '../window/resource-view-admission'
|
||||
} from '../window/visibility/resource-view-admission'
|
||||
import type { ResourceViewAdmission } from '../../shared/resource-view-admission'
|
||||
|
||||
export function registerMemoryHandlers(store: Store): void {
|
||||
|
||||
@@ -4,8 +4,8 @@ import type {
|
||||
NotificationDispatchResult,
|
||||
NotificationSettings
|
||||
} from '../../shared/notification-settings-types'
|
||||
import { safelyRevealWindow } from '../window/focus-existing-window'
|
||||
import { isBackgroundLaunch } from '../window/foreground-activation-policy'
|
||||
import { safelyRevealWindow } from '../window/activation/focus-existing-window'
|
||||
import { isBackgroundLaunch } from '../window/activation/foreground-activation-policy'
|
||||
import { getRepoIdFromWorktreeId } from '../../shared/worktree/id'
|
||||
import { parsePaneKey } from '../../shared/workspace-layout/stable-pane-id'
|
||||
import type { buildNotificationOptions } from './notification-options'
|
||||
|
||||
@@ -14,7 +14,7 @@ import { isNotificationAttentionAcknowledgement } from '../../shared/notificatio
|
||||
import type { OrcaRuntimeService } from '../runtime/orca-runtime'
|
||||
import { readNotificationAuthorizationStatus } from './notification-authorization-status'
|
||||
import { setTrayAttention } from '../tray/system-tray'
|
||||
import { isMainWindowVisible } from '../window/main-window-visibility'
|
||||
import { isMainWindowVisible } from '../window/visibility/main-window-visibility'
|
||||
import { activeNotificationsById } from './native-notification-lifecycle'
|
||||
import { deliverNativeNotification } from './native-notification-delivery'
|
||||
import { removeReadMacosNotifications } from './macos-notification-center'
|
||||
|
||||
@@ -2,7 +2,7 @@ import { getPtyIpc } from '../pty-host-bindings'
|
||||
import type { Store } from '../../../persistence/loading-store/store'
|
||||
import type { OrcaRuntimeService } from '../../../runtime/orca-runtime'
|
||||
import { rekeyMovedPaneOwners } from '../../../runtime/terminal/identity/moved-pane-owner-rekey'
|
||||
import { commitRelayedPaneMove } from '../../../window/terminal-pane-move-commit-fence'
|
||||
import { commitRelayedPaneMove } from '../../../window/renderer-relays/terminal-pane-move-commit-fence'
|
||||
import {
|
||||
isTerminalLeafMoveRequest,
|
||||
type TerminalLeafMoveRequest,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ResourceViewAdmission } from '../../../../shared/resource-view-admission'
|
||||
import { assertResourceViewVisible } from '../../../window/resource-view-admission'
|
||||
import { assertResourceViewVisible } from '../../../window/visibility/resource-view-admission'
|
||||
import { getPtyIpc } from '../pty-host-bindings'
|
||||
import {
|
||||
PtyProcessListAdmission,
|
||||
|
||||
@@ -78,11 +78,9 @@ import { registerMiniMaxCredentialsHandlers } from '../minimax-credentials'
|
||||
import { registerZcodePlanCredentialsHandlers } from '../zcode-plan-credentials'
|
||||
import { registerGrokAccountHandlers } from '../grok-accounts'
|
||||
import { registerCursorAccountHandlers } from '../cursor-accounts'
|
||||
import { registerUpdaterHandlers } from '../../window/attach-main-window-services'
|
||||
import {
|
||||
registerClipboardHandlers,
|
||||
setTrustedClipboardRendererWebContentsId
|
||||
} from '../../window/clipboard-ipc-handlers'
|
||||
import { registerUpdaterHandlers } from '../../updater/lifecycle/main-window-updater'
|
||||
import { registerClipboardHandlers } from '../../window/clipboard/clipboard-ipc-handlers'
|
||||
import { setTrustedClipboardRendererWebContentsId } from '../../window/clipboard/clipboard-ipc-sender-trust'
|
||||
import { isDashboardPopoutRenderer } from '../../window/dashboard-popout-window'
|
||||
import type { RateLimitService } from '../../rate-limits/service/rate-limit-service'
|
||||
import type { CodexAccountService } from '../../codex-accounts/service'
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
getWorkspaceSpaceWindow,
|
||||
isWorkspaceSpaceWindowRenderer
|
||||
} from '../../window/workspace-space-window'
|
||||
import { safelyRevealWindow } from '../../window/focus-existing-window'
|
||||
import { isBackgroundLaunch } from '../../window/foreground-activation-policy'
|
||||
import { safelyRevealWindow } from '../../window/activation/focus-existing-window'
|
||||
import { isBackgroundLaunch } from '../../window/activation/foreground-activation-policy'
|
||||
import { normalizeExecutionHostId } from '../../../shared/execution-host'
|
||||
import { isWorkspaceSpaceChange } from '../../../shared/workspace-disk-space/workspace-space-change'
|
||||
import type { Store } from '../../persistence/loading-store/store'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
isMainWindowVisible,
|
||||
onMainWindowBecameVisible
|
||||
} from '../../../window/main-window-visibility'
|
||||
} from '../../../window/visibility/main-window-visibility'
|
||||
import type {
|
||||
WorktreeBaseRepoWatchConfig,
|
||||
WorktreeBaseWatchTarget
|
||||
|
||||
@@ -78,7 +78,7 @@ import { listRemoteOrcadRepositories } from '../../managed-server/migration/link
|
||||
import { stageRemoteOrcadMigrationBlobChunk } from '../../managed-server/migration/transfers/orcad-migration-chunk-client'
|
||||
import { listRemoteOrcadFolderCatalog } from '../../managed-server/migration/linking/orcad-server-folder-workspaces'
|
||||
import { makeTerminalScrollbackSnapshotRef } from '../../pty/scrollback/terminal-scrollback-snapshots'
|
||||
import { pauseWorkspacesForProjectMove } from '../../window/project-move-pause'
|
||||
import { pauseWorkspacesForProjectMove } from '../../window/renderer-relays/project-move-pause'
|
||||
import { getWslHostService } from '../../wsl/host-lifecycle/wsl-host-service-instance'
|
||||
import { readWslGuestProcessTable } from '../../wsl/execution/wsl-guest-process-table'
|
||||
import { isWslDistroRegistered } from '../../wsl/discovery/wsl-distro-registry-id'
|
||||
|
||||
@@ -7,7 +7,7 @@ import { renameFileWithWindowsRetryAsync } from '../../shared/windows-retry-file
|
||||
import {
|
||||
ensureOwnedTempStagingRoot,
|
||||
getOwnedTempStagingRoot
|
||||
} from '../window/owned-temp-staging-root'
|
||||
} from '../filesystem/owned-temp-staging-root'
|
||||
|
||||
const claims = new Map<string, object>()
|
||||
const publications = new Map<string, Promise<void>>()
|
||||
|
||||
+4
-4
@@ -2,11 +2,11 @@ import { mkdirSync, mkdtempSync, realpathSync, rmSync, symlinkSync, writeFileSyn
|
||||
import { tmpdir } from 'node:os'
|
||||
import path from 'node:path'
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
||||
import { installFakeAppEnvironment } from '../../../config/scripts/test-runtime/environment/vitest-host-ports-setup'
|
||||
import { installFakeAppEnvironment } from '../../../../config/scripts/test-runtime/environment/vitest-host-ports-setup'
|
||||
|
||||
import type { Store } from '../persistence/loading-store/store'
|
||||
import { resolveLocalFileRequestPath } from '../ipc/filesystem/access/local-file-access-resolution'
|
||||
import { readLocalFileContent } from '../ipc/filesystem/filesystem-file-content-inspection'
|
||||
import type { Store } from '../../persistence/loading-store/store'
|
||||
import { resolveLocalFileRequestPath } from '../../ipc/filesystem/access/local-file-access-resolution'
|
||||
import { readLocalFileContent } from '../../ipc/filesystem/filesystem-file-content-inspection'
|
||||
import { restoreNativeChatPastes } from './native-chat-paste-files'
|
||||
|
||||
// A store with no projects: nothing but an access kind decides what a read may reach.
|
||||
+2
-2
@@ -2,8 +2,8 @@
|
||||
|
||||
import { lstat, realpath, stat } from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { getAppEnvironment } from '../../shared/app-environment'
|
||||
import { NATIVE_CHAT_PASTE_FOLDER } from '../../shared/native-chat/input/native-chat-paste-folder'
|
||||
import { getAppEnvironment } from '../../../shared/app-environment'
|
||||
import { NATIVE_CHAT_PASTE_FOLDER } from '../../../shared/native-chat/input/native-chat-paste-folder'
|
||||
|
||||
const MAX_RESTORED_PASTES = 256
|
||||
|
||||
@@ -6,7 +6,7 @@ import { z } from 'zod'
|
||||
import { isSafeOwnedDirectory } from '../../shared/owned-private-directory'
|
||||
import { isProcessAlive } from '../../shared/serve-bundle-coordination'
|
||||
import { removeTree } from '../../shared/windows-transient-lock-removal'
|
||||
import { isDirectChild } from '../window/owned-temp-staging-root'
|
||||
import { isDirectChild } from '../filesystem/owned-temp-staging-root'
|
||||
import { readRuntimeMetadataAsync } from '../runtime/rpc/server/runtime-metadata'
|
||||
import { sendOrcadSidecarRequest } from './orcad-sidecar-runtime-client'
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { BrowserWindow, Menu, webContents as electronWebContents } from 'electro
|
||||
import type { Session, WebContents } from 'electron'
|
||||
import type { KeybindingOverrides } from '../../../../shared/keybindings'
|
||||
import { setupGuestShortcutForwarding } from '../../../browser/guest-lifecycle/input/shortcuts/browser-guest-shortcut-forwarding'
|
||||
import { buildEditableContextMenuTemplate } from '../../../window/editable-context-menu'
|
||||
import { buildEditableContextMenuTemplate } from '../../../window/editing/editable-context-menu'
|
||||
import { isAllowedPluginFrameNavigation } from './plugin-frame-request-policy'
|
||||
|
||||
/** `did-attach-webview`: per-guest fences the session cannot express, plus Orca's own input. */
|
||||
|
||||
-25
@@ -1,6 +1,5 @@
|
||||
/** An incomplete GC live set deletes owned history; regressions here caused the #14863 revert (#14975). */
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { folderWorkspaceKey } from '../../shared/workspace-scope'
|
||||
import { getKnownWorktreeIdsForHistoryGc } from './history-gc-worktree-ids'
|
||||
|
||||
const noOtherProfiles = () => ({ ids: new Set<string>(), unreadableProfiles: 0 })
|
||||
@@ -19,32 +18,8 @@ describe('getKnownWorktreeIdsForHistoryGc', () => {
|
||||
})
|
||||
|
||||
// Folder PTYs mint history under folder:<id>; omitting their separate store collection falsely marks live history orphaned.
|
||||
it('includes folder workspaces under their PTY workspace key', () => {
|
||||
const live = getKnownWorktreeIdsForHistoryGc(
|
||||
store(['repo-1::/path/wt'], ['fw-1', 'fw-2']),
|
||||
noOtherProfiles
|
||||
)
|
||||
|
||||
expect(live.has(folderWorkspaceKey('fw-1'))).toBe(true)
|
||||
expect(live.has(folderWorkspaceKey('fw-2'))).toBe(true)
|
||||
expect(live.size).toBe(3)
|
||||
})
|
||||
|
||||
it('is empty only when there is genuinely nothing live', () => {
|
||||
expect(getKnownWorktreeIdsForHistoryGc(store([], []), noOtherProfiles).size).toBe(0)
|
||||
})
|
||||
|
||||
// History is not profile-scoped, so other profiles must remain in the live set.
|
||||
it('includes worktrees owned by other profiles', () => {
|
||||
const live = getKnownWorktreeIdsForHistoryGc(store(['repo-1::/b'], ['fw-b']), () => ({
|
||||
ids: new Set(['repo-1::/a', folderWorkspaceKey('fw-a')]),
|
||||
unreadableProfiles: 0
|
||||
}))
|
||||
|
||||
expect(live).toEqual(
|
||||
new Set(['repo-1::/b', folderWorkspaceKey('fw-b'), 'repo-1::/a', folderWorkspaceKey('fw-a')])
|
||||
)
|
||||
})
|
||||
|
||||
// Unreadable profiles make ownership unknown; empty means prune nothing, while partial IDs would delete live history.
|
||||
it('reports nothing live when a profile could not be read', () => {
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
import type { Store } from '../persistence/loading-store/store'
|
||||
import { folderWorkspaceKey } from '../../shared/workspace-scope'
|
||||
import { getOtherProfileWorktreeIdsForHistoryGc } from '../orca-profiles/catalog/other-profile-workspace-catalog'
|
||||
import type { Store } from '../../persistence/loading-store/store'
|
||||
import { folderWorkspaceKey } from '../../../shared/workspace-scope'
|
||||
import { getOtherProfileWorktreeIdsForHistoryGc } from '../../orca-profiles/catalog/other-profile-workspace-catalog'
|
||||
|
||||
/** Include git and folder:<id> PTY workspace keys; omitting folder workspaces makes their live shell history appear orphaned. */
|
||||
export function getKnownWorktreeIdsForHistoryGc(
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { vi } from 'vitest'
|
||||
import type { AgentBrowserBridge } from '../../../browser/agent-browser/agent-browser-bridge'
|
||||
import type { RuntimeBrowserCommandHost } from '../commands/browser-command-contracts'
|
||||
import { RuntimeBrowserPageRegistry } from './runtime-browser-page-registry'
|
||||
import type { RendererPublicationThrottleTarget } from '../../../window/renderer-publication-throttle'
|
||||
import type { RendererPublicationThrottleTarget } from '../../../window/visibility/renderer-publication-throttle'
|
||||
|
||||
// The part of the authoritative window a browser stream touches: its renderer's throttle lease target.
|
||||
export type AuthoritativeWindowDouble = {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { BrowserError } from '../../../browser/browser-error'
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { startBrowserScreencast } from '../../../browser/cdp/screencast/browser-screencast-stream'
|
||||
import { browserManager } from '../../../browser/guest-lifecycle/manager/browser-manager'
|
||||
import { rendererPublicationThrottle } from '../../../window/renderer-publication-throttle'
|
||||
import { rendererPublicationThrottle } from '../../../window/visibility/renderer-publication-throttle'
|
||||
import { mintBrowserScreencastSubscriptionId } from './browser-screencast-subscription-id'
|
||||
import {
|
||||
cancelScreencastSubscriberFrameRetry,
|
||||
|
||||
@@ -8,7 +8,7 @@ const { saveClipboardImageBufferAsTempFile } = vi.hoisted(() => ({
|
||||
saveClipboardImageBufferAsTempFile: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('../../../../window/clipboard-image-temp-file', () => ({
|
||||
vi.mock('../../../../window/clipboard/clipboard-image-temp-file', () => ({
|
||||
saveClipboardImageBufferAsTempFile
|
||||
}))
|
||||
import '../../../../../shared/rpc-contract/clipboard-params'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { defineMethod, type RpcContext } from '../../core'
|
||||
import { saveClipboardImageBufferAsTempFile } from '../../../../window/clipboard-image-temp-file'
|
||||
import { saveClipboardImageBufferAsTempFile } from '../../../../window/clipboard/clipboard-image-temp-file'
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { recordMobileClipboardImagePath } from '../../clipboard/mobile-clipboard-image-provenance'
|
||||
import {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { dialog, type BrowserWindow, type MessageBoxOptions } from 'electron'
|
||||
import type { RuntimeRpcStartErrorClass } from '../../../../shared/telemetry-daemon-event-schemas'
|
||||
import { translateMain } from '../../../i18n/main-i18n'
|
||||
import { track } from '../../../telemetry/client'
|
||||
import { waitForWindowToShow } from '../../../window/wait-for-window-to-show'
|
||||
import { waitForWindowToShow } from '../../../window/visibility/wait-for-window-to-show'
|
||||
|
||||
const MAX_VISIBLE_CAUSE_LENGTH = 500
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
} from './mobile-session-tab-close-outcome'
|
||||
import { getRuntimeBrowserPageRegistry } from '../../browser/client-pages/runtime-browser-page-registry'
|
||||
import type { RuntimeEdgeCommandSurface } from '../../construction/runtime-edge-command-controller'
|
||||
import { rendererPublicationThrottle } from '../../../window/renderer-publication-throttle'
|
||||
import { rendererPublicationThrottle } from '../../../window/visibility/renderer-publication-throttle'
|
||||
import { retireHeadlessMobileSessionEditorTab } from '../../session-tabs/headless-editor-tabs'
|
||||
import { LOCAL_EXECUTION_HOST_ID } from '../../../../shared/execution-host'
|
||||
import { ORCA_LAYOUT_OWNER_IS_MAIN } from '../../../../shared/layout-owner-switch'
|
||||
|
||||
@@ -14,7 +14,7 @@ import { isWslUncPath } from '../../../../shared/wsl-paths'
|
||||
import { isTuiAgent } from '../../../../shared/agent-launch/catalog/tui-agent-config'
|
||||
import { applyRemoteViewerLaunchEnv } from './remote-viewer-launch-env'
|
||||
import { deterministicAgentSessionUuid } from './runtime-agent-launch-resolution'
|
||||
import { rendererPublicationThrottle } from '../../../window/renderer-publication-throttle'
|
||||
import { rendererPublicationThrottle } from '../../../window/visibility/renderer-publication-throttle'
|
||||
import { PROJECT_RUNTIME_REPAIR_REQUIRED_CODE } from '../../../../shared/project-runtime-repair-message'
|
||||
import { OrchestrationError } from '../../orchestration/orchestration-error'
|
||||
import { LOCAL_EXECUTION_HOST_ID } from '../../../../shared/execution-host'
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
MOBILE_TERMINAL_SURFACE_TIMEOUT_MS
|
||||
} from '../view/terminal-surface-deadlines'
|
||||
import { isClientDisconnectedError } from '../../client/runtime-client-disconnect'
|
||||
import { rendererPublicationThrottle } from '../../../window/renderer-publication-throttle'
|
||||
import { rendererPublicationThrottle } from '../../../window/visibility/renderer-publication-throttle'
|
||||
import { deterministicAgentSessionUuid } from './runtime-agent-launch-resolution'
|
||||
import { assertMobileTerminalShellSupported } from './mobile-terminal-shell-refusal'
|
||||
import { anchorTabOfCreate, mainPlacedAfter } from './mobile-terminal-create-anchor'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { app, dialog } from 'electron'
|
||||
import { prepareEarlyMainUiLanguage, translateMain } from '../i18n/main-i18n'
|
||||
import { formatProfileStateStartupFailure } from '../persistence/profile-state/startup/profile-state-startup-failure'
|
||||
import { isBackgroundLaunch } from '../window/foreground-activation-policy'
|
||||
import { isBackgroundLaunch } from '../window/activation/foreground-activation-policy'
|
||||
import { mainProcessState as state } from './main-process-state'
|
||||
import { SINGLE_INSTANCE_ALREADY_RUNNING_EXIT_CODE } from '../../shared/single-instance-exit-code'
|
||||
import { acquireDesktopProfileInstanceLock } from './profile-startup/desktop-profile-instance-lock'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { app, session } from 'electron'
|
||||
import { applyBackgroundActivationPolicy } from '../window/foreground-activation-policy'
|
||||
import { applyBackgroundActivationPolicy } from '../window/activation/foreground-activation-policy'
|
||||
import { applyElectronProxySettings } from '../network/proxy-settings'
|
||||
import { installElectronProxyRequestGuard } from '../network/electron-proxy-request-guard'
|
||||
import { handleElectronProxyLogin } from '../network/electron-proxy-credentials'
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
sendOpenFeatureTour,
|
||||
sendOpenSetupGuide
|
||||
} from './main-window-actions'
|
||||
import { ensureAutoUpdaterConfigured } from '../../window/main-window-updater'
|
||||
import { ensureAutoUpdaterConfigured } from '../../updater/lifecycle/main-window-updater'
|
||||
import { logStartupMilestone } from '../diagnostics/startup-diagnostics'
|
||||
import { browserManager } from '../../browser/guest-lifecycle/manager/browser-manager'
|
||||
|
||||
|
||||
@@ -40,10 +40,10 @@ vi.mock('../../tray/system-tray', () => ({
|
||||
createSystemTray: vi.fn(),
|
||||
setMacMenuBarIconVisible: vi.fn()
|
||||
}))
|
||||
vi.mock('../../window/main-window-updater', () => ({
|
||||
vi.mock('../../updater/lifecycle/main-window-updater', () => ({
|
||||
ensureAutoUpdaterConfigured: vi.fn()
|
||||
}))
|
||||
vi.mock('../../window/focus-existing-window', () => ({
|
||||
vi.mock('../../window/activation/focus-existing-window', () => ({
|
||||
focusExistingMainWindow: vi.fn(),
|
||||
safelyRevealWindow: vi.fn()
|
||||
}))
|
||||
@@ -53,12 +53,14 @@ vi.mock('../windows-install-permissions/windows-install-dir-acl-recovery', () =>
|
||||
describeInstallDirAclPoison: vi.fn(),
|
||||
isBlockingInstallDirAclRepairInFlight: vi.fn()
|
||||
}))
|
||||
vi.mock('../../window/renderer-recovery-prompt', () => ({ presentRendererRecoveryPrompt: vi.fn() }))
|
||||
vi.mock('../../window/renderer-launch-failure-probe', () => ({
|
||||
vi.mock('../../window/renderer-recovery/renderer-recovery-prompt', () => ({
|
||||
presentRendererRecoveryPrompt: vi.fn()
|
||||
}))
|
||||
vi.mock('../../window/renderer-recovery/renderer-launch-failure-probe', () => ({
|
||||
probeRendererLaunchCapacity: vi.fn()
|
||||
}))
|
||||
|
||||
import { safelyRevealWindow } from '../../window/focus-existing-window'
|
||||
import { safelyRevealWindow } from '../../window/activation/focus-existing-window'
|
||||
import {
|
||||
consumeQuitAndStopAllSessionsRequest,
|
||||
quitAfterRendererStoppedAllSessions,
|
||||
|
||||
@@ -20,13 +20,16 @@ import {
|
||||
setMacMenuBarIconVisible,
|
||||
type SystemTrayOptions
|
||||
} from '../../tray/system-tray'
|
||||
import { ensureAutoUpdaterConfigured } from '../../window/main-window-updater'
|
||||
import { focusExistingMainWindow, safelyRevealWindow } from '../../window/focus-existing-window'
|
||||
import { ensureAutoUpdaterConfigured } from '../../updater/lifecycle/main-window-updater'
|
||||
import {
|
||||
focusExistingMainWindow,
|
||||
safelyRevealWindow
|
||||
} from '../../window/activation/focus-existing-window'
|
||||
import { mainProcessState as state } from '../main-process-state'
|
||||
import { quitTeardownStartGate } from '../quit/quit-teardown-start-gate'
|
||||
import { getDashboardPopoutWindow } from '../../window/dashboard-popout-window'
|
||||
import { getWorkspaceSpaceWindow } from '../../window/workspace-space-window'
|
||||
import { markUserQuitWindowClose } from '../../window/user-quit-window-close'
|
||||
import { markUserQuitWindowClose } from '../../window/main-window/user-quit-window-close'
|
||||
import { loadMainWindow } from '../../window/createMainWindow'
|
||||
import {
|
||||
describeInstallDirAclPoison,
|
||||
@@ -35,8 +38,8 @@ import {
|
||||
import {
|
||||
presentRendererRecoveryPrompt,
|
||||
type RendererRecoveryPromptFailure
|
||||
} from '../../window/renderer-recovery-prompt'
|
||||
import { probeRendererLaunchCapacity } from '../../window/renderer-launch-failure-probe'
|
||||
} from '../../window/renderer-recovery/renderer-recovery-prompt'
|
||||
import { probeRendererLaunchCapacity } from '../../window/renderer-recovery/renderer-launch-failure-probe'
|
||||
import {
|
||||
confirmQuitAndStopAllSessions,
|
||||
explainStopAllSessionsUnavailable
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
startWindowsInstallDirAclRepairIfPoisoned
|
||||
} from '../windows-install-permissions/windows-install-dir-acl-recovery'
|
||||
import { logStartupMilestone } from '../diagnostics/startup-diagnostics'
|
||||
import { notifyMainWindowBecameVisible } from '../../window/main-window-visibility'
|
||||
import { notifyMainWindowBecameVisible } from '../../window/visibility/main-window-visibility'
|
||||
import { setTrayAttention } from '../../tray/system-tray'
|
||||
import { createSystemTrayDeferred, showRendererRecoveryPrompt } from './main-window-actions'
|
||||
import { attachMainWindowCoreServices } from './main-window-core-services'
|
||||
@@ -37,14 +37,14 @@ import {
|
||||
stopSyntheticTitleSpinnerTimer
|
||||
} from './synthetic-title-runtime'
|
||||
import { requireMainWindowServices } from './main-window-service-readiness'
|
||||
import { recordRendererLaunchFailureProbe } from '../../window/renderer-launch-failure-probe'
|
||||
import { recordRendererLaunchFailureProbe } from '../../window/renderer-recovery/renderer-launch-failure-probe'
|
||||
import { trackBrowserExtensionMainWindow } from '../../browser/extensions/tabs-and-windows/browser-extension-window-model'
|
||||
|
||||
import { isResourceWindowVisible } from '../../window/resource-view-admission'
|
||||
import { isResourceWindowVisible } from '../../window/visibility/resource-view-admission'
|
||||
import {
|
||||
applyBackgroundActivationPolicy,
|
||||
applyServeDesktopWindowActivationPolicy
|
||||
} from '../../window/foreground-activation-policy'
|
||||
} from '../../window/activation/foreground-activation-policy'
|
||||
|
||||
const TRAY_CREATE_FALLBACK_MS = 12_000
|
||||
const AGENT_STATE_CRASH_BREADCRUMB_MIN_INTERVAL_MS = 30_000
|
||||
|
||||
@@ -51,7 +51,7 @@ vi.mock('electron', () => ({
|
||||
app: mocks,
|
||||
dialog: { showMessageBox: mocks.showMessageBox }
|
||||
}))
|
||||
vi.mock('../../window/foreground-activation-policy', () => ({
|
||||
vi.mock('../../window/activation/foreground-activation-policy', () => ({
|
||||
applyBackgroundActivationPolicy: mocks.background
|
||||
}))
|
||||
vi.mock('node:fs', async (original) => {
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
import { prepareEarlyMainUiLanguage, translateMain } from '../../i18n/main-i18n'
|
||||
import { acquireProfileStateMaintenance } from '../../persistence/profile-state/access/profile-state-access'
|
||||
import { rollbackProfileState } from '../../persistence/profile-state/recovery/profile-state-recovery-command'
|
||||
import { applyBackgroundActivationPolicy } from '../../window/foreground-activation-policy'
|
||||
import { applyBackgroundActivationPolicy } from '../../window/activation/foreground-activation-policy'
|
||||
import { acquireSingleInstanceLock } from './single-instance-lock'
|
||||
|
||||
/** The process owning both locks performs recovery before Electron can initialize a runtime. */
|
||||
|
||||
@@ -2,8 +2,8 @@ import { app, dialog, type BrowserWindow, type MessageBoxOptions } from 'electro
|
||||
import { translateMain } from '../../i18n/main-i18n'
|
||||
import { profileStateWriterFailureOutcome } from '../../persistence/profile-state/writer/profile-state-writer-errors'
|
||||
import { recordProfileStateWriteFailureReport } from '../../persistence/profile-state/writer/profile-state-writer-diagnostics'
|
||||
import { isBackgroundLaunch } from '../../window/foreground-activation-policy'
|
||||
import { waitForWindowToShow } from '../../window/wait-for-window-to-show'
|
||||
import { isBackgroundLaunch } from '../../window/activation/foreground-activation-policy'
|
||||
import { waitForWindowToShow } from '../../window/visibility/wait-for-window-to-show'
|
||||
import { mainProcessState } from '../main-process-state'
|
||||
|
||||
let presenting = false
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from '../../persistence/loading-store/user-data-path'
|
||||
import { OrcaRuntimeRpcServer } from '../../runtime/rpc/server/runtime-rpc'
|
||||
import { resolveServePinnedBindHost } from '../serve-launch/serve-pinned-bind-host'
|
||||
import { isOrcadSidecarLaunch } from '../../window/foreground-activation-policy'
|
||||
import { isOrcadSidecarLaunch } from '../../window/activation/foreground-activation-policy'
|
||||
import { registerMobileHandlers } from '../../ipc/mobile'
|
||||
import { createRuntimeEnvironmentMobileRelayHosts } from '../../ipc/runtime-environments/runtime-environment-mobile-relay-hosts'
|
||||
import { getLocalPtyProvider } from '../../ipc/pty/provider/registry'
|
||||
@@ -49,7 +49,7 @@ import {
|
||||
} from '../cloud-startup/main-process-connector-startup'
|
||||
import { mainProcessState as state } from '../main-process-state'
|
||||
import { logStartupMilestone } from '../diagnostics/startup-diagnostics'
|
||||
import { setupHeadlessServeAutoUpdater } from '../../window/main-window-updater'
|
||||
import { setupHeadlessServeAutoUpdater } from '../../updater/lifecycle/main-window-updater'
|
||||
import { preserveStateBeforeUpdateRestart } from '../../updater/update-restart-preservation'
|
||||
import { resolveUpdateInstallMode } from '../../updater/updater-service'
|
||||
import { scheduleAgentLaunchRecordWarmup } from '../agent-accounts/agent-launch-record-warmup'
|
||||
|
||||
+6
-6
@@ -1,8 +1,8 @@
|
||||
import { EventEmitter } from 'node:events'
|
||||
import type { BrowserWindow } from 'electron'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { Store } from '../persistence/loading-store/store'
|
||||
import type { UpdaterSetupOptions } from '../updater/lifecycle/updater-setup'
|
||||
import type { Store } from '../../persistence/loading-store/store'
|
||||
import type { UpdaterSetupOptions } from './updater-setup'
|
||||
|
||||
const { setupMock, milestoneMock, getMainWindowMock } = vi.hoisted(() => ({
|
||||
getMainWindowMock: vi.fn<() => BrowserWindow | null>(() => null),
|
||||
@@ -11,15 +11,15 @@ const { setupMock, milestoneMock, getMainWindowMock } = vi.hoisted(() => ({
|
||||
}))
|
||||
|
||||
vi.mock('electron', () => ({ app: {}, ipcMain: {} }))
|
||||
vi.mock('../updater/updater-service', () => ({ setupAutoUpdater: setupMock }))
|
||||
vi.mock('../updater/lifecycle/electron-updater-proxy-session', () => ({
|
||||
vi.mock('../updater-service', () => ({ setupAutoUpdater: setupMock }))
|
||||
vi.mock('./electron-updater-proxy-session', () => ({
|
||||
applyProxyToElectronUpdaterSession: vi.fn()
|
||||
}))
|
||||
vi.mock('../ipc/ui', () => ({
|
||||
vi.mock('../../ipc/ui', () => ({
|
||||
isTrustedUIRenderer: () => true,
|
||||
getTrustedUIRendererWindow: getMainWindowMock
|
||||
}))
|
||||
vi.mock('../startup/diagnostics/startup-diagnostics', () => ({
|
||||
vi.mock('../../startup/diagnostics/startup-diagnostics', () => ({
|
||||
logStartupMilestone: milestoneMock
|
||||
}))
|
||||
|
||||
+15
-15
@@ -1,14 +1,17 @@
|
||||
import { app, ipcMain } from 'electron'
|
||||
import type { BrowserWindow, IpcMainInvokeEvent } from 'electron'
|
||||
import type { ReleaseBuildListResult, UpdateCheckOptions } from '../../shared/update-status-types'
|
||||
import { RELEASE_CHANNELS, type ReleaseChannel } from '../../shared/release-channel'
|
||||
import { getTrustedUIRendererWindow, isTrustedUIRenderer } from '../ipc/ui'
|
||||
import type { Store } from '../persistence/loading-store/store'
|
||||
import { logStartupMilestone } from '../startup/diagnostics/startup-diagnostics'
|
||||
import { applyProxyToElectronUpdaterSession } from '../updater/lifecycle/electron-updater-proxy-session'
|
||||
import { updaterErrorMessage } from '../updater/lifecycle/updater-coded-error'
|
||||
import { updaterCodedMessage } from '../../shared/updater-failure-copy'
|
||||
import { normalizeUpdateDelayDays } from '../../shared/update-delay'
|
||||
import type {
|
||||
ReleaseBuildListResult,
|
||||
UpdateCheckOptions
|
||||
} from '../../../shared/update-status-types'
|
||||
import { RELEASE_CHANNELS, type ReleaseChannel } from '../../../shared/release-channel'
|
||||
import { getTrustedUIRendererWindow, isTrustedUIRenderer } from '../../ipc/ui'
|
||||
import type { Store } from '../../persistence/loading-store/store'
|
||||
import { logStartupMilestone } from '../../startup/diagnostics/startup-diagnostics'
|
||||
import { applyProxyToElectronUpdaterSession } from './electron-updater-proxy-session'
|
||||
import { updaterErrorMessage } from './updater-coded-error'
|
||||
import { updaterCodedMessage } from '../../../shared/updater-failure-copy'
|
||||
import { normalizeUpdateDelayDays } from '../../../shared/update-delay'
|
||||
import {
|
||||
checkForUpdatesFromMenu,
|
||||
dismissAvailableUpdate,
|
||||
@@ -20,12 +23,9 @@ import {
|
||||
quitAndInstall,
|
||||
setupAutoUpdater,
|
||||
showLinuxPackage
|
||||
} from '../updater/updater-service'
|
||||
import type {
|
||||
PreQuitCleanupFailureMode,
|
||||
UpdateInstallMode
|
||||
} from '../updater/lifecycle/updater-state'
|
||||
import type { UpdaterSetupOptions } from '../updater/lifecycle/updater-setup'
|
||||
} from '../updater-service'
|
||||
import type { PreQuitCleanupFailureMode, UpdateInstallMode } from './updater-state'
|
||||
import type { UpdaterSetupOptions } from './updater-setup'
|
||||
|
||||
const UPDATER_SETUP_FALLBACK_MS = 15_000
|
||||
|
||||
+13
-24
@@ -1,7 +1,7 @@
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { IpcMainInvokeEvent, WebContents } from 'electron'
|
||||
import type { Store } from '../persistence/loading-store/store'
|
||||
import type * as IpcUiModule from '../ipc/ui'
|
||||
import type { Store } from '../../persistence/loading-store/store'
|
||||
import type * as IpcUiModule from '../../ipc/ui'
|
||||
|
||||
const {
|
||||
handleMock,
|
||||
@@ -31,27 +31,27 @@ vi.mock('electron', () => ({
|
||||
powerMonitor: { on: vi.fn(), off: vi.fn() }
|
||||
}))
|
||||
|
||||
vi.mock('../ipc/repos', () => ({ registerRepoHandlers: vi.fn() }))
|
||||
vi.mock('../ipc/worktrees', () => ({ registerWorktreeHandlers: vi.fn() }))
|
||||
vi.mock('../ipc/worktrees/listing/worktree-change-invalidators', () => ({
|
||||
vi.mock('../../ipc/repos', () => ({ registerRepoHandlers: vi.fn() }))
|
||||
vi.mock('../../ipc/worktrees', () => ({ registerWorktreeHandlers: vi.fn() }))
|
||||
vi.mock('../../ipc/worktrees/listing/worktree-change-invalidators', () => ({
|
||||
runWorktreeChangeInvalidators: vi.fn()
|
||||
}))
|
||||
vi.mock('../ipc/pty/provider/registry', () => ({ getLocalPtyProvider: vi.fn() }))
|
||||
vi.mock('../ipc/pty/register-handlers', () => ({ registerPtyHandlers: vi.fn() }))
|
||||
vi.mock('../memory/hydrate-local-pty-registry', () => ({
|
||||
vi.mock('../../ipc/pty/provider/registry', () => ({ getLocalPtyProvider: vi.fn() }))
|
||||
vi.mock('../../ipc/pty/register-handlers', () => ({ registerPtyHandlers: vi.fn() }))
|
||||
vi.mock('../../memory/hydrate-local-pty-registry', () => ({
|
||||
hydrateLocalPtyRegistryAtBoot: vi.fn()
|
||||
}))
|
||||
vi.mock('../browser/guest-lifecycle/manager/browser-manager', () => ({
|
||||
vi.mock('../../browser/guest-lifecycle/manager/browser-manager', () => ({
|
||||
browserManager: { unregisterAll: vi.fn() }
|
||||
}))
|
||||
vi.mock('../macos-permissions/macos-tcc-prompt-notice', () => ({
|
||||
vi.mock('../../macos-permissions/macos-tcc-prompt-notice', () => ({
|
||||
acknowledgePendingTccPromptNotice: vi.fn(),
|
||||
consumePendingTccPromptNotice: vi.fn(),
|
||||
dismissTccPromptNotice: vi.fn(),
|
||||
releasePendingTccPromptNotice: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('../updater/updater-service', () => ({
|
||||
vi.mock('../updater-service', () => ({
|
||||
checkForUpdates: vi.fn(),
|
||||
checkForUpdatesFromMenu: vi.fn(),
|
||||
downloadUpdate: vi.fn(),
|
||||
@@ -65,7 +65,7 @@ vi.mock('../updater/updater-service', () => ({
|
||||
}))
|
||||
|
||||
// Keep the seam mock for wiring proof but delegate to the real predicate for sender-authorization branches.
|
||||
vi.mock('../ipc/ui', async (importOriginal) => ({
|
||||
vi.mock('../../ipc/ui', async (importOriginal) => ({
|
||||
...(await importOriginal<typeof IpcUiModule>()),
|
||||
isTrustedUIRenderer: (sender: unknown) => isTrustedUIRendererMock(sender)
|
||||
}))
|
||||
@@ -120,7 +120,7 @@ function expectBothChannelsRejected(sender: Partial<WebContents>): void {
|
||||
describe('updater linux package recovery IPC handlers', () => {
|
||||
beforeAll(async () => {
|
||||
// Read the real predicate using the same trusted-ID state these tests set.
|
||||
actualUi = await vi.importActual<typeof IpcUiModule>('../ipc/ui')
|
||||
actualUi = await vi.importActual<typeof IpcUiModule>('../../ipc/ui')
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -141,17 +141,6 @@ describe('updater linux package recovery IPC handlers', () => {
|
||||
vi.unstubAllEnvs()
|
||||
})
|
||||
|
||||
it('serves the current main UI renderer', async () => {
|
||||
const event = senderEvent(webContents({}))
|
||||
|
||||
await expect(getHandler(RECOVERY_CHANNELS[0])(event)).resolves.toEqual({
|
||||
ok: true,
|
||||
command: "sudo apt install -- '<pkg>'",
|
||||
packageFileName: 'p'
|
||||
})
|
||||
await expect(getHandler(RECOVERY_CHANNELS[1])(event)).resolves.toBeUndefined()
|
||||
})
|
||||
|
||||
// Exercise distinct real authorization branches, not relabelled mock verdicts.
|
||||
it.each([
|
||||
['a guest webview', webContents({ getType: () => 'webview' })],
|
||||
+2
-36
@@ -1,43 +1,9 @@
|
||||
import type { BrowserWindow } from 'electron'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
vi.mock('electron', () => ({ app: { dock: { hide: vi.fn() }, setActivationPolicy: vi.fn() } }))
|
||||
|
||||
const {
|
||||
applyBackgroundActivationPolicy,
|
||||
applyServeDesktopWindowActivationPolicy,
|
||||
showWindowWithoutStealingFocus
|
||||
} = await import('./foreground-activation-policy')
|
||||
|
||||
function makeWindow(destroyed = false): BrowserWindow & {
|
||||
show: ReturnType<typeof vi.fn>
|
||||
showInactive: ReturnType<typeof vi.fn>
|
||||
} {
|
||||
return {
|
||||
isDestroyed: () => destroyed,
|
||||
show: vi.fn(() => {}),
|
||||
showInactive: vi.fn(() => {})
|
||||
} as unknown as BrowserWindow & {
|
||||
show: ReturnType<typeof vi.fn>
|
||||
showInactive: ReturnType<typeof vi.fn>
|
||||
}
|
||||
}
|
||||
|
||||
describe('showWindowWithoutStealingFocus', () => {
|
||||
it('never reveals an explicitly background window', () => {
|
||||
const window = makeWindow()
|
||||
showWindowWithoutStealingFocus(window, { ORCA_BACKGROUND_LAUNCH: '1' })
|
||||
expect(window.showInactive).not.toHaveBeenCalled()
|
||||
expect(window.show).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('still reveals explicitly headful E2E without activation', () => {
|
||||
const window = makeWindow()
|
||||
showWindowWithoutStealingFocus(window, { ORCA_E2E_HEADFUL: '1' })
|
||||
expect(window.showInactive).toHaveBeenCalledOnce()
|
||||
expect(window.show).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
const { applyBackgroundActivationPolicy, applyServeDesktopWindowActivationPolicy } =
|
||||
await import('./foreground-activation-policy')
|
||||
|
||||
describe('applyBackgroundActivationPolicy', () => {
|
||||
function makeApp() {
|
||||
@@ -36,23 +36,21 @@ import { openCodeHookService, openCode2HookService } from '../opencode/status-ho
|
||||
import { listLiveDaemonPtyIds } from '../daemon/providers/lifecycle/daemon-provider-state'
|
||||
import { hydrateLocalPtyRegistryAtBoot } from '../memory/hydrate-local-pty-registry'
|
||||
import type { ClaudeRuntimeAuthPreparation } from '../claude-accounts/runtime-auth/runtime-auth-types'
|
||||
import { getKnownWorktreeIdsForHistoryGc } from './history-gc-worktree-ids'
|
||||
import { getKnownWorktreeIdsForHistoryGc } from '../pty/history/history-gc-worktree-ids'
|
||||
import type { ClaudeAccountSelectionTarget } from '../claude-accounts/selection/runtime-selection'
|
||||
import {
|
||||
scheduleWorktreeBaseDirectoryWatcherSync,
|
||||
setWorktreeBaseDirectoryWatcherSyncContext
|
||||
} from '../ipc/worktrees/creation/worktree-base-directory-watcher'
|
||||
import { startFolderRepoGitUpgradeWatch } from '../ipc/folder-repo-git-upgrade'
|
||||
import { scheduleMainWindowAutoUpdaterSetup } from './main-window-updater'
|
||||
import { scheduleMainWindowAutoUpdaterSetup } from '../updater/lifecycle/main-window-updater'
|
||||
import { registerRuntimeWindowLifecycle } from './runtime-window-lifecycle'
|
||||
import { ensureStructuredAgentSessionHostUnlessRefused } from '../runtime/agent-session/structured-agent-session-host-refusal'
|
||||
import { getAgentSessionAttachmentStore } from '../native-chat/agent-session-attachments/agent-session-attachment-store-registry'
|
||||
import { registerDroppedPathPreparation } from './dropped-path-preparation-ipc'
|
||||
import { isPluginFramePermissionRequest } from './host-frame-permission-policy'
|
||||
import { registerDroppedPathPreparation } from './file-drop/dropped-path-preparation-ipc'
|
||||
import { isPluginFramePermissionRequest } from './security/host-frame-permission-policy'
|
||||
import { installPluginDesktopFrameServices } from '../plugins/panels/desktop-actions/plugin-frame-desktop-services'
|
||||
|
||||
export { ensureAutoUpdaterConfigured, registerUpdaterHandlers } from './main-window-updater'
|
||||
|
||||
let appReloadHandlerTokenCounter = 0
|
||||
let activeAppReloadHandlerToken: number | null = null
|
||||
let tccPromptHandlerTokenCounter = 0
|
||||
|
||||
+6
-8
@@ -3,7 +3,7 @@ import { mkdtemp, readFile, rm } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { AgentSessionAttachmentStore } from '../native-chat/agent-session-attachments/agent-session-attachment-store'
|
||||
import { AgentSessionAttachmentStore } from '../../native-chat/agent-session-attachments/agent-session-attachment-store'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
handle: vi.fn(),
|
||||
@@ -21,22 +21,20 @@ vi.mock('electron', () => ({
|
||||
ipcMain: { removeHandler: vi.fn(), handle: mocks.handle },
|
||||
nativeImage: { createFromBuffer: vi.fn() }
|
||||
}))
|
||||
vi.mock('../ipc/runtime-environments/runtime-environment-transport-routing', () => ({
|
||||
vi.mock('../../ipc/runtime-environments/runtime-environment-transport-routing', () => ({
|
||||
callRuntimeEnvironment: mocks.callRuntimeEnvironment
|
||||
}))
|
||||
vi.mock('../../shared/remote-runtime/host-store/runtime-environment-store', () => ({
|
||||
vi.mock('../../../shared/remote-runtime/host-store/runtime-environment-store', () => ({
|
||||
resolveEnvironment: (_userDataPath: string, selector: string) => ({ id: selector })
|
||||
}))
|
||||
vi.mock('./clipboard-remote-file-copy', () => ({
|
||||
cleanupExpiredRemoteClipboardFiles: vi.fn(async () => {}),
|
||||
scheduleLegacyRemoteClipboardFileCleanup: vi.fn()
|
||||
}))
|
||||
vi.mock('./dashboard-popout-window', () => ({ isDashboardPopoutRenderer: () => false }))
|
||||
vi.mock('../dashboard-popout-window', () => ({ isDashboardPopoutRenderer: () => false }))
|
||||
|
||||
import {
|
||||
registerClipboardHandlers,
|
||||
setTrustedClipboardRendererWebContentsId
|
||||
} from './clipboard-ipc-handlers'
|
||||
import { registerClipboardHandlers } from './clipboard-ipc-handlers'
|
||||
import { setTrustedClipboardRendererWebContentsId } from './clipboard-ipc-sender-trust'
|
||||
|
||||
function saveHandler(): (...args: unknown[]) => Promise<unknown> {
|
||||
const entry = mocks.handle.mock.calls.find(
|
||||
+3
-5
@@ -36,16 +36,14 @@ vi.mock('electron', () => ({
|
||||
nativeImage: { createFromBuffer: vi.fn() }
|
||||
}))
|
||||
|
||||
vi.mock('./dashboard-popout-window', () => ({ isDashboardPopoutRenderer }))
|
||||
vi.mock('../dashboard-popout-window', () => ({ isDashboardPopoutRenderer }))
|
||||
vi.mock('./clipboard-remote-file-copy', () => ({
|
||||
cleanupExpiredRemoteClipboardFiles: vi.fn(async () => undefined),
|
||||
scheduleLegacyRemoteClipboardFileCleanup: vi.fn()
|
||||
}))
|
||||
|
||||
import {
|
||||
registerClipboardHandlers,
|
||||
setTrustedClipboardRendererWebContentsId
|
||||
} from './clipboard-ipc-handlers'
|
||||
import { registerClipboardHandlers } from './clipboard-ipc-handlers'
|
||||
import { setTrustedClipboardRendererWebContentsId } from './clipboard-ipc-sender-trust'
|
||||
|
||||
const popoutEvent = {
|
||||
sender: {
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { isAbsolute } from 'node:path'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import type { ProcessResult, ProcessSpec } from '@orca/process-host/process-spec'
|
||||
import { windowsPowerShellPath } from '@orca/process-host/windows-system-binary'
|
||||
import { withTimeout } from '../../shared/promise-timeout-fallback'
|
||||
import { withTimeout } from '../../../shared/promise-timeout-fallback'
|
||||
|
||||
export type ClipboardFileResult = { ok: boolean; reason?: string }
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import type { NativeImage } from 'electron'
|
||||
import { encodeClipboardImageWithinLimit } from '../../shared/clipboard-image'
|
||||
import { encodeClipboardImageWithinLimit } from '../../../shared/clipboard-image'
|
||||
|
||||
export function encodeClipboardNativeImagePng(
|
||||
image: Pick<NativeImage, 'getSize' | 'resize' | 'toPNG'>
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { clipboardFormatsIncludeImage } from '../../shared/clipboard-image'
|
||||
import { clipboardFormatsIncludeImage } from '../../../shared/clipboard-image'
|
||||
import {
|
||||
readWindowsCopiedImageFilePath,
|
||||
type WindowsClipboardFileFormats
|
||||
+6
-6
@@ -2,16 +2,16 @@ import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { randomUUID } from 'node:crypto'
|
||||
|
||||
import { SshHostNotServedError } from '../providers/ssh-host-not-served-error'
|
||||
import { getAppEnvironment } from '../../shared/app-environment'
|
||||
import { assertClipboardImageByteLengthWithinLimit } from '../../shared/clipboard-image'
|
||||
import { SshHostNotServedError } from '../../providers/ssh-host-not-served-error'
|
||||
import { getAppEnvironment } from '../../../shared/app-environment'
|
||||
import { assertClipboardImageByteLengthWithinLimit } from '../../../shared/clipboard-image'
|
||||
import {
|
||||
agentSessionPastedImageName,
|
||||
sanitizeAgentSessionAttachmentName,
|
||||
type AgentSessionAttachmentClipboardTarget
|
||||
} from '../../shared/agent-session/agent-session-attachments'
|
||||
import { detectImageFileExtension } from '../../shared/image-file-signature'
|
||||
import { nativeChatPasteFolder } from './native-chat-paste-files'
|
||||
} from '../../../shared/agent-session/agent-session-attachments'
|
||||
import { detectImageFileExtension } from '../../../shared/image-file-signature'
|
||||
import { nativeChatPasteFolder } from '../../native-chat/paste-files/native-chat-paste-files'
|
||||
|
||||
export type SaveClipboardImageAsTempFileArgs = {
|
||||
connectionId?: string | null
|
||||
+1
-1
@@ -2,7 +2,7 @@ import {
|
||||
clipboardImageThumbnailSize,
|
||||
type ClipboardImageDimensions,
|
||||
type ClipboardImageThumbnail
|
||||
} from '../../shared/clipboard-image'
|
||||
} from '../../../shared/clipboard-image'
|
||||
|
||||
/** Minimal NativeImage surface for tests without an Electron runtime. */
|
||||
export type ClipboardImageLike = {
|
||||
+12
-14
@@ -2,10 +2,10 @@ import { app, clipboard, ipcMain, nativeImage } from 'electron'
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { open, stat } from 'node:fs/promises'
|
||||
import { runProcess } from '@orca/process-host'
|
||||
import type { Store } from '../persistence/loading-store/store'
|
||||
import { PATH_ACCESS_DENIED_MESSAGE } from '../ipc/filesystem/access/filesystem-auth'
|
||||
import { resolveDesktopAuthorizedPath } from '../ipc/filesystem/access/local-file-access-resolution'
|
||||
import { isENOENT } from '../ipc/filesystem/access/filesystem-path-containment'
|
||||
import type { Store } from '../../persistence/loading-store/store'
|
||||
import { PATH_ACCESS_DENIED_MESSAGE } from '../../ipc/filesystem/access/filesystem-auth'
|
||||
import { resolveDesktopAuthorizedPath } from '../../ipc/filesystem/access/local-file-access-resolution'
|
||||
import { isENOENT } from '../../ipc/filesystem/access/filesystem-path-containment'
|
||||
import {
|
||||
assertTrustedClipboardSender,
|
||||
assertTrustedClipboardTextSender
|
||||
@@ -14,8 +14,8 @@ import {
|
||||
assertClipboardTextWriteWithinLimitWithYield,
|
||||
assertClipboardTextWithinLimitWithYield,
|
||||
type ReadClipboardTextOptions
|
||||
} from '../../shared/clipboard-text'
|
||||
import { clipboardHtmlMayHaveLinks } from '../../shared/clipboard-link-html'
|
||||
} from '../../../shared/clipboard-text'
|
||||
import { clipboardHtmlMayHaveLinks } from '../../../shared/clipboard-link-html'
|
||||
import {
|
||||
saveClipboardImageBufferAsTempFile,
|
||||
type SaveClipboardImageAsTempFileArgs
|
||||
@@ -25,19 +25,19 @@ import {
|
||||
assertClipboardImageByteLengthWithinLimit,
|
||||
assertClipboardImageDimensionsWithinLimit,
|
||||
type ClipboardImageThumbnail
|
||||
} from '../../shared/clipboard-image'
|
||||
} from '../../../shared/clipboard-image'
|
||||
import {
|
||||
writeFileToClipboard,
|
||||
type ClipboardFileDeps,
|
||||
type ClipboardFileResult
|
||||
} from './clipboard-file-copy'
|
||||
import { SshHostNotServedError } from '../providers/ssh-host-not-served-error'
|
||||
import { SshHostNotServedError } from '../../providers/ssh-host-not-served-error'
|
||||
import {
|
||||
cleanupExpiredRemoteClipboardFiles,
|
||||
scheduleLegacyRemoteClipboardFileCleanup
|
||||
} from './clipboard-remote-file-copy'
|
||||
import { saveClipboardImageBufferInRuntime } from './clipboard-runtime-image-upload'
|
||||
import { uploadPasteToAgentSessionAttachments } from '../ipc/agent-session-attachment-upload'
|
||||
import { uploadPasteToAgentSessionAttachments } from '../../ipc/agent-session-attachment-upload'
|
||||
import { readWindowsClipboardImageFileAsPng } from './clipboard-windows-image-file'
|
||||
import { readClipboardImageSource } from './clipboard-image-source'
|
||||
import { readClipboardCopiedFilePaths } from './clipboard-copied-file-paths'
|
||||
@@ -50,12 +50,10 @@ import {
|
||||
publishClipboardOperation,
|
||||
publishClipboardText
|
||||
} from './clipboard-text-publisher'
|
||||
import { restoreNativeChatPastes } from './native-chat-paste-files'
|
||||
import { restoreNativeChatPastes } from '../../native-chat/paste-files/native-chat-paste-files'
|
||||
import { savePastedTextAsFile, type SavePastedTextAsFileArgs } from './native-chat-pasted-text-file'
|
||||
import { agentSessionPastedImageName } from '../../shared/agent-session/agent-session-attachments'
|
||||
import type { AgentSessionAttachmentStore } from '../native-chat/agent-session-attachments/agent-session-attachment-store'
|
||||
|
||||
export { setTrustedClipboardRendererWebContentsId } from './clipboard-ipc-sender-trust'
|
||||
import { agentSessionPastedImageName } from '../../../shared/agent-session/agent-session-attachments'
|
||||
import type { AgentSessionAttachmentStore } from '../../native-chat/agent-session-attachments/agent-session-attachment-store'
|
||||
|
||||
type ClipboardWriteFileRequest = {
|
||||
filePath: string
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import type { IpcMainInvokeEvent, WebContents } from 'electron'
|
||||
import { isTrustedMainRendererSender } from '../ipc/main-renderer-sender-trust'
|
||||
import { isDashboardPopoutRenderer } from './dashboard-popout-window'
|
||||
import { isTrustedMainRendererSender } from '../../ipc/main-renderer-sender-trust'
|
||||
import { isDashboardPopoutRenderer } from '../dashboard-popout-window'
|
||||
|
||||
let trustedClipboardRendererWebContentsId: number | null = null
|
||||
|
||||
+8
-10
@@ -1,7 +1,7 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type * as FsPromises from 'node:fs/promises'
|
||||
import { installFakeAppEnvironment } from '../../../config/scripts/test-runtime/environment/vitest-host-ports-setup'
|
||||
import '../../shared/clipboard-image'
|
||||
import { installFakeAppEnvironment } from '../../../../config/scripts/test-runtime/environment/vitest-host-ports-setup'
|
||||
import '../../../shared/clipboard-image'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
handlers: new Map<string, (...args: unknown[]) => unknown>(),
|
||||
@@ -32,22 +32,20 @@ vi.mock('node:fs/promises', async (importOriginal) => {
|
||||
const fs = await importOriginal<typeof FsPromises>()
|
||||
return { ...fs, default: { ...fs, writeFile: mocks.writeFile } }
|
||||
})
|
||||
vi.mock('../persistence/loading-store/store', () => ({ Store: vi.fn() }))
|
||||
vi.mock('../../persistence/loading-store/store', () => ({ Store: vi.fn() }))
|
||||
vi.mock('./clipboard-remote-file-copy', () => ({
|
||||
cleanupExpiredRemoteClipboardFiles: vi.fn(),
|
||||
scheduleLegacyRemoteClipboardFileCleanup: vi.fn()
|
||||
}))
|
||||
vi.mock('./native-chat-paste-files', () => ({
|
||||
vi.mock('../../native-chat/paste-files/native-chat-paste-files', () => ({
|
||||
restoreNativeChatPastes: vi.fn(),
|
||||
nativeChatPasteFolder: () => '/tmp/native-chat-pastes'
|
||||
}))
|
||||
vi.mock('./dashboard-popout-window', () => ({ isDashboardPopoutRenderer: () => false }))
|
||||
vi.mock('../dashboard-popout-window', () => ({ isDashboardPopoutRenderer: () => false }))
|
||||
|
||||
import { Store } from '../persistence/loading-store/store'
|
||||
import {
|
||||
registerClipboardHandlers,
|
||||
setTrustedClipboardRendererWebContentsId
|
||||
} from './clipboard-ipc-handlers'
|
||||
import { Store } from '../../persistence/loading-store/store'
|
||||
import { registerClipboardHandlers } from './clipboard-ipc-handlers'
|
||||
import { setTrustedClipboardRendererWebContentsId } from './clipboard-ipc-sender-trust'
|
||||
|
||||
const event = {
|
||||
sender: {
|
||||
+2
-2
@@ -2,8 +2,8 @@ import { clipboard, nativeImage, type NativeImage } from 'electron'
|
||||
import {
|
||||
assertClipboardImageByteLengthWithinLimit,
|
||||
assertClipboardImageDimensionsWithinLimit
|
||||
} from '../../shared/clipboard-image'
|
||||
import { readRasterImageDimensions } from '../../shared/file-preview/raster-image-dimensions'
|
||||
} from '../../../shared/clipboard-image'
|
||||
import { readRasterImageDimensions } from '../../../shared/file-preview/raster-image-dimensions'
|
||||
|
||||
export function readClipboardNativeImage(): NativeImage {
|
||||
const image = clipboard.readImage()
|
||||
+2
-2
@@ -5,10 +5,10 @@ import {
|
||||
getOwnedTempStagingRoot,
|
||||
isDirectChild,
|
||||
isMissingPathError,
|
||||
isSafeOwnedDirectory,
|
||||
removeOwnedDirectory,
|
||||
sweepExpiredOwnedDirectories
|
||||
} from './owned-temp-staging-root'
|
||||
} from '../../filesystem/owned-temp-staging-root'
|
||||
import { isSafeOwnedDirectory } from '../../../shared/owned-private-directory'
|
||||
|
||||
const REMOTE_CLIPBOARD_STAGING_ROOT_NAME = 'orca-clipboard-files'
|
||||
const REMOTE_CLIPBOARD_LEGACY_PREFIX = 'orca-clipboard-file-'
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { assertClipboardImageByteLengthWithinLimit } from '../../shared/clipboard-image'
|
||||
import { callRuntimeEnvironment } from '../ipc/runtime-environments/runtime-environment-transport-routing'
|
||||
import { assertClipboardImageByteLengthWithinLimit } from '../../../shared/clipboard-image'
|
||||
import { callRuntimeEnvironment } from '../../ipc/runtime-environments/runtime-environment-transport-routing'
|
||||
|
||||
const CLIPBOARD_IMAGE_UPLOAD_CHUNK_BASE64_CHARS = 512 * 1024
|
||||
const CLIPBOARD_IMAGE_SINGLE_FRAME_FALLBACK_BASE64_CHARS = 256 * 1024
|
||||
+2
-103
@@ -2,10 +2,9 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { ProcessResult, ProcessSpec } from '@orca/process-host/process-spec'
|
||||
import {
|
||||
assertClipboardTextWriteWithinLimitWithYield,
|
||||
CLIPBOARD_TEXT_MEASURE_YIELD_CODE_UNITS,
|
||||
CLIPBOARD_TEXT_WRITE_MAX_BYTES
|
||||
} from '../../shared/clipboard-text'
|
||||
import { CLIPBOARD_IMAGE_MAX_BASE64_CHARS } from '../../shared/clipboard-image'
|
||||
} from '../../../shared/clipboard-text'
|
||||
import { CLIPBOARD_IMAGE_MAX_BASE64_CHARS } from '../../../shared/clipboard-image'
|
||||
import { createClipboardPublisher } from './clipboard-text-publisher'
|
||||
|
||||
vi.mock('electron', () => ({ app: {}, clipboard: {} }))
|
||||
@@ -34,38 +33,6 @@ describe('terminal clipboard writes', () => {
|
||||
run.mockResolvedValue(ok)
|
||||
})
|
||||
|
||||
it('publishes different and duplicate text without asking Electron to own the selection', async () => {
|
||||
const copy = writer()
|
||||
const text = '遥か\r\nline two\n'
|
||||
await copy(text)
|
||||
await copy(text)
|
||||
expect(run).toHaveBeenCalledTimes(2)
|
||||
expect(run).toHaveBeenCalledWith({
|
||||
program: '/bundle/bin/orca-wayland-clipboard',
|
||||
input: text,
|
||||
timeoutMs: 2000,
|
||||
stdio: ['pipe', 'ignore', 'ignore']
|
||||
})
|
||||
expect(write).not.toHaveBeenCalled()
|
||||
expect(clearCache).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('only discards a rejected Chromium source after the new owner is acknowledged', async () => {
|
||||
let release: (value: typeof ok) => void = () => undefined
|
||||
run.mockImplementationOnce(
|
||||
() =>
|
||||
new Promise((resolve) => {
|
||||
release = resolve
|
||||
})
|
||||
)
|
||||
const pending = writer()('copy')
|
||||
await vi.waitFor(() => expect(run).toHaveBeenCalledTimes(1))
|
||||
expect(clearCache).not.toHaveBeenCalled()
|
||||
release(ok)
|
||||
await pending
|
||||
expect(clearCache).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it.each([
|
||||
['timeout', { ...ok, code: null, timedOut: true }],
|
||||
['helper failure', { ...ok, code: 70 }]
|
||||
@@ -76,20 +43,6 @@ describe('terminal clipboard writes', () => {
|
||||
expect(clearCache).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('preserves the existing clipboard when the bundled helper cannot start', async () => {
|
||||
run.mockRejectedValueOnce(new Error('ENOENT'))
|
||||
await expect(writer()('copy')).rejects.toThrow('ENOENT')
|
||||
expect(write).not.toHaveBeenCalled()
|
||||
expect(clearCache).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('retains verified Electron writes outside native Wayland', async () => {
|
||||
await writer(false)('copy')
|
||||
expect(write).toHaveBeenCalledWith('copy', 'terminal')
|
||||
expect(run).not.toHaveBeenCalled()
|
||||
expect(clearCache).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('rejects oversized text before either clipboard backend is invoked', async () => {
|
||||
await expect(writer()('x'.repeat(CLIPBOARD_TEXT_WRITE_MAX_BYTES + 1))).rejects.toThrow(
|
||||
'Clipboard text is too large'
|
||||
@@ -98,60 +51,6 @@ describe('terminal clipboard writes', () => {
|
||||
expect(write).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it.each([false, true])(
|
||||
'keeps a newer copy after yielding validation (Wayland=%s)',
|
||||
async (wayland) => {
|
||||
vi.useFakeTimers()
|
||||
try {
|
||||
const copy = writer(wayland)
|
||||
const large = 'x'.repeat(CLIPBOARD_TEXT_MEASURE_YIELD_CODE_UNITS * 2)
|
||||
const first = copy(large, 'terminal')
|
||||
const second = copy('newer', 'ordinary')
|
||||
|
||||
await vi.runAllTimersAsync()
|
||||
await Promise.all([first, second])
|
||||
|
||||
if (wayland) {
|
||||
expect(run.mock.calls.map(([spec]) => spec.input === large)).toEqual([true, false])
|
||||
expect(run.mock.calls[1]?.[0].input).toBe('newer')
|
||||
expect(write).not.toHaveBeenCalled()
|
||||
} else {
|
||||
expect(write.mock.calls.map(([text, kind]) => [text === large, kind])).toEqual([
|
||||
[true, 'terminal'],
|
||||
[false, 'ordinary']
|
||||
])
|
||||
expect(write.mock.calls[1]?.[0]).toBe('newer')
|
||||
expect(run).not.toHaveBeenCalled()
|
||||
}
|
||||
} finally {
|
||||
vi.useRealTimers()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
it('preserves request order across yielding validation and resumes after failure', async () => {
|
||||
let release: (text: string) => void = () => undefined
|
||||
const validate = vi.fn(async (text: string) => text)
|
||||
validate.mockImplementationOnce(
|
||||
() =>
|
||||
new Promise((resolve) => {
|
||||
release = resolve
|
||||
})
|
||||
)
|
||||
run.mockRejectedValueOnce(new Error('helper failed'))
|
||||
const copy = writer(true, validate)
|
||||
const first = copy('first')
|
||||
const firstRejected = expect(first).rejects.toThrow('helper failed')
|
||||
const second = copy('second')
|
||||
await vi.waitFor(() => expect(validate).toHaveBeenCalledTimes(1))
|
||||
expect(run).not.toHaveBeenCalled()
|
||||
release('first')
|
||||
await firstRejected
|
||||
await second
|
||||
expect(run.mock.calls.map(([spec]) => spec.input)).toEqual(['first', 'second'])
|
||||
expect(clearCache).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it.each([
|
||||
{ label: 'count', count: 32, bytes: 1 },
|
||||
{ label: 'retained bytes', count: 1, bytes: CLIPBOARD_IMAGE_MAX_BASE64_CHARS * 2 }
|
||||
+5
-5
@@ -5,13 +5,13 @@ import {
|
||||
assertClipboardTextWriteWithinLimitWithYield,
|
||||
CLIPBOARD_TEXT_WRITE_MAX_BYTES,
|
||||
CLIPBOARD_TEXT_WRITE_TOO_LARGE_ERROR
|
||||
} from '../../shared/clipboard-text'
|
||||
import { CLIPBOARD_IMAGE_MAX_BASE64_CHARS } from '../../shared/clipboard-image'
|
||||
import { isLinuxWaylandSession } from '../../shared/linux-wayland-session'
|
||||
} from '../../../shared/clipboard-text'
|
||||
import { CLIPBOARD_IMAGE_MAX_BASE64_CHARS } from '../../../shared/clipboard-image'
|
||||
import { isLinuxWaylandSession } from '../../../shared/linux-wayland-session'
|
||||
import { writeClipboardTextAndVerify } from './clipboard-text-write-verify'
|
||||
import { stageClipboardPublisher } from './clipboard-publisher-executable'
|
||||
import { envWithoutAppImageRuntime } from '../pty/shell-startup/appimage-terminal-env'
|
||||
import { isLegacyAppImageRuntimeIdentity } from '../updater/linux-package/linux-update-package-type'
|
||||
import { envWithoutAppImageRuntime } from '../../pty/shell-startup/appimage-terminal-env'
|
||||
import { isLegacyAppImageRuntimeIdentity } from '../../updater/linux-package/linux-update-package-type'
|
||||
|
||||
const WAYLAND_CLIPBOARD_WRITE_FAILED_ERROR = 'Wayland clipboard write failed'
|
||||
const MAX_PENDING_CLIPBOARD_WRITES = 32
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user