mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 08:02:28 +00:00
perf(browser): fill plain fields in one stdin edit (#20364)
This commit is contained in:
@@ -6,14 +6,10 @@ import type {
|
||||
} from '../../shared/runtime-types'
|
||||
import { assertClipboardTextWriteWithinLimitWithYield } from '../../shared/clipboard-text'
|
||||
import { normalizeBrowserNavigationUrl } from '../../shared/browser-url'
|
||||
import { iterateBrowserTextInsertionChunks } from './browser-text-insertion'
|
||||
import { BrowserError } from './cdp-bridge'
|
||||
import { ORCA_TAB_SESSION_PREFIX } from './agent-browser-orphan-sweep'
|
||||
import { focusedValueSetExpression } from './agent-browser-bridge-input'
|
||||
import {
|
||||
AGENT_BROWSER_TEXT_ARGUMENT_MAX_BYTES,
|
||||
EMBEDDED_NAVIGATION_TIMEOUT_MS
|
||||
} from './agent-browser-bridge-types'
|
||||
import { EMBEDDED_NAVIGATION_TIMEOUT_MS } from './agent-browser-bridge-types'
|
||||
import {
|
||||
isAbortedNavigationError,
|
||||
waitForAbortedNavigationReplacement
|
||||
@@ -157,23 +153,10 @@ export abstract class AgentBrowserBridgeCoreCommands extends AgentBrowserBridgeQ
|
||||
async (sessionName) => {
|
||||
if (!(await this.isExplicitContentEditableTarget(sessionName, element))) {
|
||||
await this.execAgentBrowser(sessionName, ['focus', element])
|
||||
await this.execAgentBrowser(sessionName, [
|
||||
'eval',
|
||||
focusedValueSetExpression(JSON.stringify(''))
|
||||
])
|
||||
for (const chunk of iterateBrowserTextInsertionChunks(
|
||||
value,
|
||||
AGENT_BROWSER_TEXT_ARGUMENT_MAX_BYTES
|
||||
)) {
|
||||
await this.execAgentBrowser(sessionName, [
|
||||
'eval',
|
||||
focusedValueSetExpression(JSON.stringify(chunk), { append: true })
|
||||
])
|
||||
}
|
||||
await this.execAgentBrowser(sessionName, [
|
||||
'eval',
|
||||
focusedValueSetExpression(JSON.stringify(''), { append: true, dispatchEvents: true })
|
||||
])
|
||||
// One stdin edit avoids argv limits and repeated copying of the growing field value.
|
||||
await this.execAgentBrowser(sessionName, ['eval', '--stdin'], {
|
||||
stdinText: focusedValueSetExpression(JSON.stringify(value), { dispatchEvents: true })
|
||||
})
|
||||
return { filled: element } as BrowserFillResult
|
||||
}
|
||||
|
||||
|
||||
@@ -278,8 +278,9 @@ describe('AgentBrowserBridge', () => {
|
||||
)
|
||||
expect(evalCall).toBeDefined()
|
||||
const args = evalCall![1] as string[]
|
||||
const expression = args[args.indexOf('eval') + 1]
|
||||
expect(() => new Function(expression)).not.toThrow()
|
||||
expect(args[args.indexOf('eval') + 1]).toBe('--stdin')
|
||||
expect(stdinWrites).toHaveLength(1)
|
||||
expect(() => new Function(stdinWrites[0])).not.toThrow()
|
||||
})
|
||||
|
||||
it('replaces contenteditable text through the browser editing pipeline', async () => {
|
||||
@@ -359,12 +360,7 @@ describe('AgentBrowserBridge', () => {
|
||||
|
||||
await bridge.fill('@spinbutton', '200')
|
||||
|
||||
const expressions = execFileMock.mock.calls
|
||||
.filter((call: unknown[]) => (call[1] as string[]).includes('eval'))
|
||||
.map((call: unknown[]) => {
|
||||
const args = call[1] as string[]
|
||||
return args[args.indexOf('eval') + 1]
|
||||
})
|
||||
const expressions = stdinWrites
|
||||
|
||||
const input = createFillEvalNode({ tagName: 'INPUT' })
|
||||
const wrapper = createFillEvalNode({
|
||||
@@ -389,12 +385,7 @@ describe('AgentBrowserBridge', () => {
|
||||
|
||||
await bridge.fill('@spinbutton', '200')
|
||||
|
||||
const expressions = execFileMock.mock.calls
|
||||
.filter((call: unknown[]) => (call[1] as string[]).includes('eval'))
|
||||
.map((call: unknown[]) => {
|
||||
const args = call[1] as string[]
|
||||
return args[args.indexOf('eval') + 1]
|
||||
})
|
||||
const expressions = stdinWrites
|
||||
|
||||
const input = createFillEvalNode({ tagName: 'INPUT' })
|
||||
const wrapper = createFillEvalNode({
|
||||
@@ -419,12 +410,7 @@ describe('AgentBrowserBridge', () => {
|
||||
|
||||
await bridge.fill('@spinbutton', '200')
|
||||
|
||||
const expressions = execFileMock.mock.calls
|
||||
.filter((call: unknown[]) => (call[1] as string[]).includes('eval'))
|
||||
.map((call: unknown[]) => {
|
||||
const args = call[1] as string[]
|
||||
return args[args.indexOf('eval') + 1]
|
||||
})
|
||||
const expressions = stdinWrites
|
||||
|
||||
const input = createFillEvalNode({ tagName: 'INPUT' })
|
||||
const controlled = createFillEvalNode({ tagName: 'DIV', descendant: input.node })
|
||||
@@ -452,12 +438,7 @@ describe('AgentBrowserBridge', () => {
|
||||
|
||||
await bridge.fill('@spinbutton', '200')
|
||||
|
||||
const expressions = execFileMock.mock.calls
|
||||
.filter((call: unknown[]) => (call[1] as string[]).includes('eval'))
|
||||
.map((call: unknown[]) => {
|
||||
const args = call[1] as string[]
|
||||
return args[args.indexOf('eval') + 1]
|
||||
})
|
||||
const expressions = stdinWrites
|
||||
|
||||
const hiddenInput = createFillEvalNode({ tagName: 'INPUT', type: 'hidden' })
|
||||
const numberInput = createFillEvalNode({ tagName: 'INPUT', type: 'number' })
|
||||
@@ -485,12 +466,7 @@ describe('AgentBrowserBridge', () => {
|
||||
|
||||
await bridge.fill('@input', '200')
|
||||
|
||||
const expressions = execFileMock.mock.calls
|
||||
.filter((call: unknown[]) => (call[1] as string[]).includes('eval'))
|
||||
.map((call: unknown[]) => {
|
||||
const args = call[1] as string[]
|
||||
return args[args.indexOf('eval') + 1]
|
||||
})
|
||||
const expressions = stdinWrites
|
||||
|
||||
const input = createFillEvalNode({ tagName: 'INPUT' })
|
||||
|
||||
@@ -503,8 +479,8 @@ describe('AgentBrowserBridge', () => {
|
||||
expect(input.events.map((event) => event.type)).toEqual(['input', 'change'])
|
||||
})
|
||||
|
||||
it('chunks large agent-browser fill values before eval transport', async () => {
|
||||
const text = ['x'.repeat(AGENT_BROWSER_TEXT_ARGUMENT_MAX_BYTES), 'tail'].join('')
|
||||
it('fills large plain fields with one stdin edit and one event pair', async () => {
|
||||
const text = `${'é\n'.repeat(512 * 1024)}tail'\\`
|
||||
succeedWith({ ok: true })
|
||||
|
||||
await bridge.fill('@textarea', text)
|
||||
@@ -512,15 +488,17 @@ describe('AgentBrowserBridge', () => {
|
||||
const evalCalls = execFileMock.mock.calls.filter((call: unknown[]) =>
|
||||
(call[1] as string[]).includes('eval')
|
||||
)
|
||||
const appendExpressions = evalCalls.slice(1, -1).map((call: unknown[]) => {
|
||||
const args = call[1] as string[]
|
||||
return args[args.indexOf('eval') + 1]
|
||||
expect(evalCalls).toHaveLength(1)
|
||||
expect(evalCalls[0][1]).toContain('--stdin')
|
||||
expect(stdinWrites).toHaveLength(1)
|
||||
expect((evalCalls[0][1] as string[]).join('')).not.toContain(text)
|
||||
const input = createFillEvalNode({ tagName: 'TEXTAREA' })
|
||||
runFillEvalExpressions(stdinWrites, {
|
||||
activeElement: input.node,
|
||||
getElementById: () => null
|
||||
})
|
||||
|
||||
expect(appendExpressions).toHaveLength(2)
|
||||
expect(appendExpressions.some((expression) => expression.includes(text))).toBe(false)
|
||||
expect(appendExpressions[0]).toContain('x'.repeat(AGENT_BROWSER_TEXT_ARGUMENT_MAX_BYTES))
|
||||
expect(appendExpressions[1]).toContain('tail')
|
||||
expect(input.value).toBe(text)
|
||||
expect(input.events.map((event) => event.type)).toEqual(['input', 'change'])
|
||||
})
|
||||
|
||||
it.each([
|
||||
|
||||
Reference in New Issue
Block a user