@monoes/monobrowse 1.0.6 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -0
- package/dist/src/__tests__/adapters.test.d.ts +2 -0
- package/dist/src/__tests__/adapters.test.d.ts.map +1 -0
- package/dist/src/__tests__/adapters.test.js +104 -0
- package/dist/src/__tests__/adapters.test.js.map +1 -0
- package/dist/src/__tests__/batch-eval-parsing.test.d.ts +0 -0
- package/dist/src/__tests__/batch-eval-parsing.test.d.ts.map +0 -0
- package/dist/src/__tests__/batch-eval-parsing.test.js +0 -0
- package/dist/src/__tests__/batch-eval-parsing.test.js.map +0 -0
- package/dist/src/__tests__/box-model.test.d.ts +0 -0
- package/dist/src/__tests__/box-model.test.d.ts.map +0 -0
- package/dist/src/__tests__/box-model.test.js +0 -0
- package/dist/src/__tests__/box-model.test.js.map +0 -0
- package/dist/src/__tests__/browser-launch.test.d.ts +0 -0
- package/dist/src/__tests__/browser-launch.test.d.ts.map +0 -0
- package/dist/src/__tests__/browser-launch.test.js +0 -0
- package/dist/src/__tests__/browser-launch.test.js.map +0 -0
- package/dist/src/__tests__/cdp-client.test.d.ts +2 -0
- package/dist/src/__tests__/cdp-client.test.d.ts.map +1 -0
- package/dist/src/__tests__/cdp-client.test.js +326 -0
- package/dist/src/__tests__/cdp-client.test.js.map +1 -0
- package/dist/src/__tests__/close-browser.test.d.ts +2 -0
- package/dist/src/__tests__/close-browser.test.d.ts.map +1 -0
- package/dist/src/__tests__/close-browser.test.js +128 -0
- package/dist/src/__tests__/close-browser.test.js.map +1 -0
- package/dist/src/__tests__/console-log.test.d.ts +2 -0
- package/dist/src/__tests__/console-log.test.d.ts.map +1 -0
- package/dist/src/__tests__/console-log.test.js +195 -0
- package/dist/src/__tests__/console-log.test.js.map +1 -0
- package/dist/src/__tests__/find.test.d.ts +2 -0
- package/dist/src/__tests__/find.test.d.ts.map +1 -0
- package/dist/src/__tests__/find.test.js +299 -0
- package/dist/src/__tests__/find.test.js.map +1 -0
- package/dist/src/__tests__/frame-command.test.d.ts +2 -0
- package/dist/src/__tests__/frame-command.test.d.ts.map +1 -0
- package/dist/src/__tests__/frame-command.test.js +85 -0
- package/dist/src/__tests__/frame-command.test.js.map +1 -0
- package/dist/src/__tests__/output.test.d.ts +0 -0
- package/dist/src/__tests__/output.test.d.ts.map +0 -0
- package/dist/src/__tests__/output.test.js +0 -0
- package/dist/src/__tests__/output.test.js.map +0 -0
- package/dist/src/__tests__/ref-cache.test.d.ts +0 -0
- package/dist/src/__tests__/ref-cache.test.d.ts.map +0 -0
- package/dist/src/__tests__/ref-cache.test.js +43 -2
- package/dist/src/__tests__/ref-cache.test.js.map +1 -1
- package/dist/src/__tests__/session-state.test.d.ts +2 -0
- package/dist/src/__tests__/session-state.test.d.ts.map +1 -0
- package/dist/src/__tests__/session-state.test.js +203 -0
- package/dist/src/__tests__/session-state.test.js.map +1 -0
- package/dist/src/__tests__/snapshot.test.d.ts +2 -0
- package/dist/src/__tests__/snapshot.test.d.ts.map +1 -0
- package/dist/src/__tests__/snapshot.test.js +262 -0
- package/dist/src/__tests__/snapshot.test.js.map +1 -0
- package/dist/src/__tests__/storage-eval.test.d.ts +2 -0
- package/dist/src/__tests__/storage-eval.test.d.ts.map +1 -0
- package/dist/src/__tests__/storage-eval.test.js +133 -0
- package/dist/src/__tests__/storage-eval.test.js.map +1 -0
- package/dist/src/__tests__/tabs.test.d.ts +2 -0
- package/dist/src/__tests__/tabs.test.d.ts.map +1 -0
- package/dist/src/__tests__/tabs.test.js +161 -0
- package/dist/src/__tests__/tabs.test.js.map +1 -0
- package/dist/src/browser/action-builder/analyzer.d.ts +0 -0
- package/dist/src/browser/action-builder/analyzer.d.ts.map +0 -0
- package/dist/src/browser/action-builder/analyzer.js +0 -0
- package/dist/src/browser/action-builder/analyzer.js.map +0 -0
- package/dist/src/browser/action-builder/types.d.ts +0 -0
- package/dist/src/browser/action-builder/types.d.ts.map +0 -0
- package/dist/src/browser/action-builder/types.js +0 -0
- package/dist/src/browser/action-builder/types.js.map +0 -0
- package/dist/src/browser/actions.d.ts +0 -0
- package/dist/src/browser/actions.d.ts.map +0 -0
- package/dist/src/browser/actions.js +0 -0
- package/dist/src/browser/actions.js.map +0 -0
- package/dist/src/browser/adapters/gemini.d.ts +0 -0
- package/dist/src/browser/adapters/gemini.d.ts.map +0 -0
- package/dist/src/browser/adapters/gemini.js +0 -0
- package/dist/src/browser/adapters/gemini.js.map +0 -0
- package/dist/src/browser/adapters/google.d.ts +0 -0
- package/dist/src/browser/adapters/google.d.ts.map +0 -0
- package/dist/src/browser/adapters/google.js +0 -0
- package/dist/src/browser/adapters/google.js.map +0 -0
- package/dist/src/browser/adapters/index.d.ts +0 -0
- package/dist/src/browser/adapters/index.d.ts.map +0 -0
- package/dist/src/browser/adapters/index.js +0 -0
- package/dist/src/browser/adapters/index.js.map +0 -0
- package/dist/src/browser/adapters/instagram.d.ts +0 -0
- package/dist/src/browser/adapters/instagram.d.ts.map +0 -0
- package/dist/src/browser/adapters/instagram.js +0 -0
- package/dist/src/browser/adapters/instagram.js.map +0 -0
- package/dist/src/browser/adapters/linkedin.d.ts +0 -0
- package/dist/src/browser/adapters/linkedin.d.ts.map +0 -0
- package/dist/src/browser/adapters/linkedin.js +0 -0
- package/dist/src/browser/adapters/linkedin.js.map +0 -0
- package/dist/src/browser/adapters/microsoft.d.ts +0 -0
- package/dist/src/browser/adapters/microsoft.d.ts.map +0 -0
- package/dist/src/browser/adapters/microsoft.js +0 -0
- package/dist/src/browser/adapters/microsoft.js.map +0 -0
- package/dist/src/browser/adapters/x.d.ts +0 -0
- package/dist/src/browser/adapters/x.d.ts.map +0 -0
- package/dist/src/browser/adapters/x.js +0 -0
- package/dist/src/browser/adapters/x.js.map +0 -0
- package/dist/src/browser/batch.d.ts +0 -0
- package/dist/src/browser/batch.d.ts.map +0 -0
- package/dist/src/browser/batch.js +0 -0
- package/dist/src/browser/batch.js.map +0 -0
- package/dist/src/browser/browser.d.ts +4 -0
- package/dist/src/browser/browser.d.ts.map +1 -1
- package/dist/src/browser/browser.js +55 -7
- package/dist/src/browser/browser.js.map +1 -1
- package/dist/src/browser/cdp.d.ts +4 -1
- package/dist/src/browser/cdp.d.ts.map +1 -1
- package/dist/src/browser/cdp.js +45 -16
- package/dist/src/browser/cdp.js.map +1 -1
- package/dist/src/browser/console-log.d.ts +0 -0
- package/dist/src/browser/console-log.d.ts.map +0 -0
- package/dist/src/browser/console-log.js +0 -0
- package/dist/src/browser/console-log.js.map +0 -0
- package/dist/src/browser/dashboard/api-types.d.ts +0 -0
- package/dist/src/browser/dashboard/api-types.d.ts.map +0 -0
- package/dist/src/browser/dashboard/api-types.js +0 -0
- package/dist/src/browser/dashboard/api-types.js.map +0 -0
- package/dist/src/browser/dashboard/server.d.ts +0 -0
- package/dist/src/browser/dashboard/server.d.ts.map +0 -0
- package/dist/src/browser/dashboard/server.js +0 -0
- package/dist/src/browser/dashboard/server.js.map +0 -0
- package/dist/src/browser/dialog.d.ts +0 -0
- package/dist/src/browser/dialog.d.ts.map +0 -0
- package/dist/src/browser/dialog.js +0 -0
- package/dist/src/browser/dialog.js.map +0 -0
- package/dist/src/browser/emulation.d.ts +0 -0
- package/dist/src/browser/emulation.d.ts.map +0 -0
- package/dist/src/browser/emulation.js +0 -0
- package/dist/src/browser/emulation.js.map +0 -0
- package/dist/src/browser/find.d.ts +0 -0
- package/dist/src/browser/find.d.ts.map +0 -0
- package/dist/src/browser/find.js +0 -0
- package/dist/src/browser/find.js.map +0 -0
- package/dist/src/browser/har.d.ts +0 -0
- package/dist/src/browser/har.d.ts.map +0 -0
- package/dist/src/browser/har.js +0 -0
- package/dist/src/browser/har.js.map +0 -0
- package/dist/src/browser/index.d.ts +0 -0
- package/dist/src/browser/index.d.ts.map +0 -0
- package/dist/src/browser/index.js +0 -0
- package/dist/src/browser/index.js.map +0 -0
- package/dist/src/browser/network.d.ts +0 -0
- package/dist/src/browser/network.d.ts.map +0 -0
- package/dist/src/browser/network.js +0 -0
- package/dist/src/browser/network.js.map +0 -0
- package/dist/src/browser/pdf.d.ts +0 -0
- package/dist/src/browser/pdf.d.ts.map +0 -0
- package/dist/src/browser/pdf.js +0 -0
- package/dist/src/browser/pdf.js.map +0 -0
- package/dist/src/browser/profiler.d.ts +0 -0
- package/dist/src/browser/profiler.d.ts.map +0 -0
- package/dist/src/browser/profiler.js +0 -0
- package/dist/src/browser/profiler.js.map +0 -0
- package/dist/src/browser/record.d.ts +0 -0
- package/dist/src/browser/record.d.ts.map +0 -0
- package/dist/src/browser/record.js +0 -0
- package/dist/src/browser/record.js.map +0 -0
- package/dist/src/browser/ref-cache.d.ts +6 -1
- package/dist/src/browser/ref-cache.d.ts.map +1 -1
- package/dist/src/browser/ref-cache.js +21 -3
- package/dist/src/browser/ref-cache.js.map +1 -1
- package/dist/src/browser/screenshot.d.ts +0 -0
- package/dist/src/browser/screenshot.d.ts.map +0 -0
- package/dist/src/browser/screenshot.js +0 -0
- package/dist/src/browser/screenshot.js.map +0 -0
- package/dist/src/browser/session.d.ts +0 -0
- package/dist/src/browser/session.d.ts.map +0 -0
- package/dist/src/browser/session.js +0 -0
- package/dist/src/browser/session.js.map +0 -0
- package/dist/src/browser/snapshot.d.ts +0 -0
- package/dist/src/browser/snapshot.d.ts.map +0 -0
- package/dist/src/browser/snapshot.js +0 -0
- package/dist/src/browser/snapshot.js.map +0 -0
- package/dist/src/browser/storage.d.ts +0 -0
- package/dist/src/browser/storage.d.ts.map +0 -0
- package/dist/src/browser/storage.js +0 -0
- package/dist/src/browser/storage.js.map +0 -0
- package/dist/src/browser/tabs.d.ts +5 -1
- package/dist/src/browser/tabs.d.ts.map +1 -1
- package/dist/src/browser/tabs.js +4 -3
- package/dist/src/browser/tabs.js.map +1 -1
- package/dist/src/browser/trace.d.ts +0 -0
- package/dist/src/browser/trace.d.ts.map +0 -0
- package/dist/src/browser/trace.js +0 -0
- package/dist/src/browser/trace.js.map +0 -0
- package/dist/src/browser/types.d.ts +4 -0
- package/dist/src/browser/types.d.ts.map +1 -1
- package/dist/src/browser/types.js +0 -0
- package/dist/src/browser/types.js.map +1 -1
- package/dist/src/browser/vitals.d.ts +0 -0
- package/dist/src/browser/vitals.d.ts.map +0 -0
- package/dist/src/browser/vitals.js +0 -0
- package/dist/src/browser/vitals.js.map +0 -0
- package/dist/src/browser/wait.d.ts +0 -0
- package/dist/src/browser/wait.d.ts.map +0 -0
- package/dist/src/browser/wait.js +0 -0
- package/dist/src/browser/wait.js.map +0 -0
- package/dist/src/cli/action.d.ts +0 -0
- package/dist/src/cli/action.d.ts.map +0 -0
- package/dist/src/cli/action.js +0 -0
- package/dist/src/cli/action.js.map +0 -0
- package/dist/src/cli/commands.d.ts +0 -0
- package/dist/src/cli/commands.d.ts.map +1 -1
- package/dist/src/cli/commands.js +96 -4
- package/dist/src/cli/commands.js.map +1 -1
- package/dist/src/cli/output.d.ts +0 -0
- package/dist/src/cli/output.d.ts.map +0 -0
- package/dist/src/cli/output.js +0 -0
- package/dist/src/cli/output.js.map +0 -0
- package/dist/src/cli/platform.d.ts +0 -0
- package/dist/src/cli/platform.d.ts.map +0 -0
- package/dist/src/cli/platform.js +0 -0
- package/dist/src/cli/platform.js.map +0 -0
- package/dist/src/cli/types.d.ts +0 -0
- package/dist/src/cli/types.d.ts.map +0 -0
- package/dist/src/cli/types.js +0 -0
- package/dist/src/cli/types.js.map +0 -0
- package/dist/src/cli.d.ts +0 -0
- package/dist/src/cli.d.ts.map +0 -0
- package/dist/src/cli.js +0 -0
- package/dist/src/cli.js.map +0 -0
- package/dist/src/index.d.ts +0 -0
- package/dist/src/index.d.ts.map +0 -0
- package/dist/src/index.js +0 -0
- package/dist/src/index.js.map +0 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -5
- package/src/__tests__/adapters.test.ts +125 -0
- package/src/__tests__/cdp-client.test.ts +375 -0
- package/src/__tests__/close-browser.test.ts +141 -0
- package/src/__tests__/console-log.test.ts +250 -0
- package/src/__tests__/find.test.ts +354 -0
- package/src/__tests__/frame-command.test.ts +98 -0
- package/src/__tests__/ref-cache.test.ts +47 -2
- package/src/__tests__/session-state.test.ts +251 -0
- package/src/__tests__/snapshot.test.ts +331 -0
- package/src/__tests__/storage-eval.test.ts +171 -0
- package/src/__tests__/tabs.test.ts +183 -0
- package/src/browser/browser.ts +59 -7
- package/src/browser/cdp.ts +52 -15
- package/src/browser/ref-cache.ts +29 -6
- package/src/browser/tabs.ts +12 -4
- package/src/browser/types.ts +4 -0
- package/src/cli/commands.ts +90 -4
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* storage.ts builds localStorage/sessionStorage expressions by string
|
|
3
|
+
* concatenation, so the JSON.stringify escaping is the only thing standing
|
|
4
|
+
* between a user-supplied key and script injection into the page. That plus
|
|
5
|
+
* evaluateJs's error/timeout contract is what this file pins down.
|
|
6
|
+
* Fake client throughout — no browser.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
9
|
+
import { evaluateJs } from '../browser/actions.js';
|
|
10
|
+
import { getLocalStorageKey, setLocalStorageKey, removeLocalStorageKey, clearLocalStorage, getAllLocalStorage, getSessionStorageKey, setSessionStorageKey, removeSessionStorageKey, clearSessionStorage, getAllSessionStorage, } from '../browser/storage.js';
|
|
11
|
+
/** Client stub returning a fixed Runtime.evaluate value, recording expressions. */
|
|
12
|
+
function stubClient(value, exceptionDetails) {
|
|
13
|
+
const exprs = [];
|
|
14
|
+
const sids = [];
|
|
15
|
+
const client = {
|
|
16
|
+
send: vi.fn(async (_m, params, sid) => {
|
|
17
|
+
exprs.push(params.expression);
|
|
18
|
+
sids.push(sid);
|
|
19
|
+
return exceptionDetails ? { result: {}, exceptionDetails } : { result: { value, type: 'string' } };
|
|
20
|
+
}),
|
|
21
|
+
};
|
|
22
|
+
return { client, exprs, sids };
|
|
23
|
+
}
|
|
24
|
+
describe('evaluateJs', () => {
|
|
25
|
+
it('sends Runtime.evaluate with returnByValue and awaitPromise, scoped to the session', async () => {
|
|
26
|
+
const client = {
|
|
27
|
+
send: vi.fn(async () => ({ result: { value: 42, type: 'number' } })),
|
|
28
|
+
};
|
|
29
|
+
await expect(evaluateJs(client, 'S1', '1 + 41')).resolves.toBe(42);
|
|
30
|
+
expect(client.send.mock.calls[0]).toEqual([
|
|
31
|
+
'Runtime.evaluate',
|
|
32
|
+
{ expression: '1 + 41', returnByValue: true, awaitPromise: true },
|
|
33
|
+
'S1',
|
|
34
|
+
]);
|
|
35
|
+
});
|
|
36
|
+
it('surfaces the exception description when the page throws', async () => {
|
|
37
|
+
const { client } = stubClient(undefined, {
|
|
38
|
+
text: 'Uncaught',
|
|
39
|
+
exception: { description: 'ReferenceError: foo is not defined' },
|
|
40
|
+
});
|
|
41
|
+
await expect(evaluateJs(client, 'S1', 'foo')).rejects.toThrow('JS evaluation error: ReferenceError: foo is not defined');
|
|
42
|
+
});
|
|
43
|
+
it('falls back to exceptionDetails.text when there is no description', async () => {
|
|
44
|
+
const { client } = stubClient(undefined, { text: 'Uncaught SyntaxError' });
|
|
45
|
+
await expect(evaluateJs(client, 'S1', '{{')).rejects.toThrow('JS evaluation error: Uncaught SyntaxError');
|
|
46
|
+
});
|
|
47
|
+
it('returns undefined for an expression with no value', async () => {
|
|
48
|
+
const client = { send: vi.fn(async () => ({ result: { type: 'undefined' } })) };
|
|
49
|
+
await expect(evaluateJs(client, 'S1', 'void 0')).resolves.toBeUndefined();
|
|
50
|
+
});
|
|
51
|
+
it('rejects rather than hanging when the page never settles the promise', async () => {
|
|
52
|
+
// awaitPromise:true means a never-settling expression would hang forever
|
|
53
|
+
// without the race; a 20ms budget proves the timer is armed.
|
|
54
|
+
const client = { send: vi.fn(() => new Promise(() => { })) };
|
|
55
|
+
await expect(evaluateJs(client, 'S1', 'new Promise(() => {})', 20)).rejects.toThrow('JS evaluation timed out after 20ms');
|
|
56
|
+
});
|
|
57
|
+
it('waits indefinitely when the timeout is disabled with 0', async () => {
|
|
58
|
+
let resolveSend = () => { };
|
|
59
|
+
const client = {
|
|
60
|
+
send: vi.fn(() => new Promise((r) => { resolveSend = r; })),
|
|
61
|
+
};
|
|
62
|
+
const p = evaluateJs(client, 'S1', 'slow()', 0);
|
|
63
|
+
resolveSend({ result: { value: 'done', type: 'string' } });
|
|
64
|
+
await expect(p).resolves.toBe('done');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe('localStorage helpers', () => {
|
|
68
|
+
it('reads a key through a JSON-escaped getItem call', async () => {
|
|
69
|
+
const { client, exprs, sids } = stubClient('v');
|
|
70
|
+
await expect(getLocalStorageKey(client, 'S1', 'token')).resolves.toBe('v');
|
|
71
|
+
expect(exprs[0]).toBe('localStorage.getItem("token")');
|
|
72
|
+
expect(sids[0]).toBe('S1');
|
|
73
|
+
});
|
|
74
|
+
it('escapes a key containing quotes and backslashes instead of breaking out', async () => {
|
|
75
|
+
const { client, exprs } = stubClient(null);
|
|
76
|
+
await getLocalStorageKey(client, 'S1', 'a"); alert(1); //');
|
|
77
|
+
expect(exprs[0]).toBe('localStorage.getItem("a\\"); alert(1); //")');
|
|
78
|
+
// The payload must survive as *one* string literal: round-tripping the
|
|
79
|
+
// argument back through JSON.parse recovers the key verbatim, which it
|
|
80
|
+
// could not if the quote had terminated the literal early.
|
|
81
|
+
const arg = exprs[0].slice('localStorage.getItem('.length, -1);
|
|
82
|
+
expect(JSON.parse(arg)).toBe('a"); alert(1); //');
|
|
83
|
+
});
|
|
84
|
+
it('escapes newlines in a key so the expression stays one statement', async () => {
|
|
85
|
+
const { client, exprs } = stubClient(null);
|
|
86
|
+
await removeLocalStorageKey(client, 'S1', 'a\nb');
|
|
87
|
+
expect(exprs[0]).toBe('localStorage.removeItem("a\\nb")');
|
|
88
|
+
expect(exprs[0]).not.toContain('\n');
|
|
89
|
+
});
|
|
90
|
+
it('escapes both key and value on write', async () => {
|
|
91
|
+
const { client, exprs } = stubClient();
|
|
92
|
+
await setLocalStorageKey(client, 'S1', 'k"1', 'v"2');
|
|
93
|
+
expect(exprs[0]).toBe('localStorage.setItem("k\\"1", "v\\"2")');
|
|
94
|
+
});
|
|
95
|
+
it('clears with a literal expression', async () => {
|
|
96
|
+
const { client, exprs } = stubClient();
|
|
97
|
+
await clearLocalStorage(client, 'S1');
|
|
98
|
+
expect(exprs[0]).toBe('localStorage.clear()');
|
|
99
|
+
});
|
|
100
|
+
it('getAll parses the JSON dump', async () => {
|
|
101
|
+
const { client } = stubClient('{"a":"1","b":"2"}');
|
|
102
|
+
await expect(getAllLocalStorage(client, 'S1')).resolves.toEqual({ a: '1', b: '2' });
|
|
103
|
+
});
|
|
104
|
+
it('getAll degrades to {} on unparseable output rather than throwing', async () => {
|
|
105
|
+
const { client } = stubClient('not json');
|
|
106
|
+
await expect(getAllLocalStorage(client, 'S1')).resolves.toEqual({});
|
|
107
|
+
});
|
|
108
|
+
it('getItem returning null (missing key) is passed through as null', async () => {
|
|
109
|
+
const { client } = stubClient(null);
|
|
110
|
+
await expect(getLocalStorageKey(client, 'S1', 'missing')).resolves.toBeNull();
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
describe('sessionStorage helpers mirror the localStorage ones', () => {
|
|
114
|
+
it('read / write / remove / clear all target sessionStorage', async () => {
|
|
115
|
+
const { client, exprs } = stubClient('v');
|
|
116
|
+
await getSessionStorageKey(client, 'S1', 'k');
|
|
117
|
+
await setSessionStorageKey(client, 'S1', 'k', 'v');
|
|
118
|
+
await removeSessionStorageKey(client, 'S1', 'k');
|
|
119
|
+
await clearSessionStorage(client, 'S1');
|
|
120
|
+
expect(exprs).toEqual([
|
|
121
|
+
'sessionStorage.getItem("k")',
|
|
122
|
+
'sessionStorage.setItem("k", "v")',
|
|
123
|
+
'sessionStorage.removeItem("k")',
|
|
124
|
+
'sessionStorage.clear()',
|
|
125
|
+
]);
|
|
126
|
+
expect(exprs.some((e) => e.includes('localStorage'))).toBe(false);
|
|
127
|
+
});
|
|
128
|
+
it('getAll parses, and degrades to {} on garbage', async () => {
|
|
129
|
+
await expect(getAllSessionStorage(stubClient('{"s":"1"}').client, 'S1')).resolves.toEqual({ s: '1' });
|
|
130
|
+
await expect(getAllSessionStorage(stubClient('<html>').client, 'S1')).resolves.toEqual({});
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
//# sourceMappingURL=storage-eval.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-eval.test.js","sourceRoot":"","sources":["../../../src/__tests__/storage-eval.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tabs.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/tabs.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tabs.ts — target filtering and the CDP command sequences for
|
|
3
|
+
* close/activate/frame-switch. Fake fetch + fake client; no browser.
|
|
4
|
+
*/
|
|
5
|
+
import { describe, it, expect, afterEach, vi } from 'vitest';
|
|
6
|
+
import { listTabs, newTab, closeTab, activateTab, switchToFrame } from '../browser/tabs.js';
|
|
7
|
+
const realFetch = globalThis.fetch;
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
globalThis.fetch = realFetch;
|
|
10
|
+
vi.restoreAllMocks();
|
|
11
|
+
});
|
|
12
|
+
function stubFetch(body, init = { status: 200 }) {
|
|
13
|
+
const fn = vi.fn(() => Promise.resolve(new Response(JSON.stringify(body), init)));
|
|
14
|
+
globalThis.fetch = fn;
|
|
15
|
+
return fn;
|
|
16
|
+
}
|
|
17
|
+
/** Client stub that records calls and answers from a per-method table. */
|
|
18
|
+
function stubClient(table = {}) {
|
|
19
|
+
const calls = [];
|
|
20
|
+
const client = {
|
|
21
|
+
send: vi.fn(async (method, params, sid) => {
|
|
22
|
+
calls.push({ method, params, sid });
|
|
23
|
+
const entry = table[method];
|
|
24
|
+
if (entry instanceof Error)
|
|
25
|
+
throw entry;
|
|
26
|
+
return entry ?? {};
|
|
27
|
+
}),
|
|
28
|
+
};
|
|
29
|
+
return { client, calls };
|
|
30
|
+
}
|
|
31
|
+
describe('listTabs', () => {
|
|
32
|
+
it('keeps only page targets, dropping service workers, iframes and extensions', async () => {
|
|
33
|
+
stubFetch([
|
|
34
|
+
{ id: 'A', type: 'page', url: 'https://x.test' },
|
|
35
|
+
{ id: 'B', type: 'service_worker', url: 'https://x.test/sw.js' },
|
|
36
|
+
{ id: 'C', type: 'iframe', url: 'https://ads.test' },
|
|
37
|
+
{ id: 'D', type: 'background_page', url: 'chrome-extension://abc' },
|
|
38
|
+
{ id: 'E', type: 'page', url: 'about:blank' },
|
|
39
|
+
]);
|
|
40
|
+
const tabs = await listTabs(9222);
|
|
41
|
+
expect(tabs.map((t) => t.id)).toEqual(['A', 'E']);
|
|
42
|
+
});
|
|
43
|
+
it('returns [] when Chrome reports no targets at all', async () => {
|
|
44
|
+
stubFetch([]);
|
|
45
|
+
await expect(listTabs(9222)).resolves.toEqual([]);
|
|
46
|
+
});
|
|
47
|
+
it('propagates a failed /json/list', async () => {
|
|
48
|
+
globalThis.fetch = vi.fn(() => Promise.resolve(new Response('', { status: 502, statusText: 'Bad Gateway' })));
|
|
49
|
+
await expect(listTabs(9222)).rejects.toThrow('Failed to fetch targets: Bad Gateway');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe('newTab', () => {
|
|
53
|
+
it('defaults to about:blank', async () => {
|
|
54
|
+
const fn = stubFetch({ id: 'T1', type: 'page' });
|
|
55
|
+
await newTab(9222);
|
|
56
|
+
expect(fn.mock.calls[0][0]).toBe('http://127.0.0.1:9222/json/new?' + encodeURIComponent('about:blank'));
|
|
57
|
+
});
|
|
58
|
+
it('passes an explicit url through, encoded', async () => {
|
|
59
|
+
const fn = stubFetch({ id: 'T1', type: 'page' });
|
|
60
|
+
await newTab(9222, 'https://x.test/a b?q=1');
|
|
61
|
+
expect(fn.mock.calls[0][0]).toContain(encodeURIComponent('https://x.test/a b?q=1'));
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
describe('closeTab', () => {
|
|
65
|
+
it('sends Target.closeTarget for the given targetId', async () => {
|
|
66
|
+
const { client, calls } = stubClient();
|
|
67
|
+
await closeTab(client, 'S1', 'T9');
|
|
68
|
+
expect(calls).toEqual([{ method: 'Target.closeTarget', params: { targetId: 'T9' }, sid: undefined }]);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
describe('activateTab', () => {
|
|
72
|
+
it('detaches the old session, activates, then re-attaches flat and returns the new sessionId', async () => {
|
|
73
|
+
const { client, calls } = stubClient({ 'Target.attachToTarget': { sessionId: 'S-NEW' } });
|
|
74
|
+
await expect(activateTab(client, 'S-OLD', 'T9')).resolves.toBe('S-NEW');
|
|
75
|
+
expect(calls.map((c) => c.method)).toEqual([
|
|
76
|
+
'Target.detachFromTarget',
|
|
77
|
+
'Target.activateTarget',
|
|
78
|
+
'Target.attachToTarget',
|
|
79
|
+
]);
|
|
80
|
+
expect(calls[0].params).toEqual({ sessionId: 'S-OLD' });
|
|
81
|
+
expect(calls[2].params).toEqual({ targetId: 'T9', flatten: true });
|
|
82
|
+
});
|
|
83
|
+
it('skips the detach when there is no previous session', async () => {
|
|
84
|
+
const { client, calls } = stubClient({ 'Target.attachToTarget': { sessionId: 'S-NEW' } });
|
|
85
|
+
await activateTab(client, '', 'T9');
|
|
86
|
+
expect(calls.map((c) => c.method)).toEqual(['Target.activateTarget', 'Target.attachToTarget']);
|
|
87
|
+
});
|
|
88
|
+
it('tolerates a detach failure on an already-dead session', async () => {
|
|
89
|
+
const calls = [];
|
|
90
|
+
const client = {
|
|
91
|
+
send: vi.fn(async (method) => {
|
|
92
|
+
calls.push(method);
|
|
93
|
+
if (method === 'Target.detachFromTarget')
|
|
94
|
+
throw new Error('No session with given id');
|
|
95
|
+
if (method === 'Target.attachToTarget')
|
|
96
|
+
return { sessionId: 'S-NEW' };
|
|
97
|
+
return {};
|
|
98
|
+
}),
|
|
99
|
+
};
|
|
100
|
+
await expect(activateTab(client, 'S-STALE', 'T9')).resolves.toBe('S-NEW');
|
|
101
|
+
expect(calls).toContain('Target.activateTarget');
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
describe('switchToFrame', () => {
|
|
105
|
+
const frameTreeWith = (url) => ({
|
|
106
|
+
frameTree: { childFrames: [{ frame: { id: 'F1', url, securityOrigin: 'https://f.test' } }] },
|
|
107
|
+
});
|
|
108
|
+
it('returns null url when the selector does not resolve to an IFRAME', async () => {
|
|
109
|
+
const { client, calls } = stubClient({
|
|
110
|
+
'Runtime.evaluate': { result: { result: { value: null } } },
|
|
111
|
+
'Page.getFrameTree': { frameTree: {} },
|
|
112
|
+
});
|
|
113
|
+
await expect(switchToFrame(client, 'S1', 'div.not-a-frame')).resolves.toEqual({ url: null, sessionId: null });
|
|
114
|
+
// The selector is embedded as a JSON string literal, never interpolated raw.
|
|
115
|
+
const expr = calls[0].params.expression;
|
|
116
|
+
expect(expr).toContain('document.querySelector("div.not-a-frame")');
|
|
117
|
+
});
|
|
118
|
+
it('JSON-escapes a selector containing quotes rather than breaking the expression', async () => {
|
|
119
|
+
const { client, calls } = stubClient({
|
|
120
|
+
'Runtime.evaluate': { result: { result: { value: null } } },
|
|
121
|
+
'Page.getFrameTree': { frameTree: {} },
|
|
122
|
+
});
|
|
123
|
+
await switchToFrame(client, 'S1', 'iframe[title="a\\"b"]');
|
|
124
|
+
const expr = calls[0].params.expression;
|
|
125
|
+
expect(expr).toContain(JSON.stringify('iframe[title="a\\"b"]'));
|
|
126
|
+
});
|
|
127
|
+
it('returns the frame src with null sessionId when no OOPIF target matches', async () => {
|
|
128
|
+
const { client, calls } = stubClient({
|
|
129
|
+
'Runtime.evaluate': { result: { result: { value: 'https://f.test/frame' } } },
|
|
130
|
+
'Page.getFrameTree': frameTreeWith('https://f.test/frame'),
|
|
131
|
+
'Target.getTargets': { targetInfos: [{ targetId: 'T1', type: 'page', url: 'https://x.test' }] },
|
|
132
|
+
});
|
|
133
|
+
await expect(switchToFrame(client, 'S1', 'iframe')).resolves.toEqual({ url: 'https://f.test/frame', sessionId: null });
|
|
134
|
+
expect(calls.map((c) => c.method)).not.toContain('Target.attachToTarget');
|
|
135
|
+
});
|
|
136
|
+
it('attaches flat to a matching OOPIF target and returns its sessionId', async () => {
|
|
137
|
+
const { client, calls } = stubClient({
|
|
138
|
+
'Runtime.evaluate': { result: { result: { value: 'https://f.test/frame' } } },
|
|
139
|
+
'Page.getFrameTree': frameTreeWith('https://f.test/frame'),
|
|
140
|
+
'Target.getTargets': {
|
|
141
|
+
targetInfos: [
|
|
142
|
+
{ targetId: 'T-PAGE', type: 'page', url: 'https://f.test/frame' },
|
|
143
|
+
{ targetId: 'T-OOPIF', type: 'iframe', url: 'https://f.test/frame' },
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
'Target.attachToTarget': { sessionId: 'S-FRAME' },
|
|
147
|
+
});
|
|
148
|
+
await expect(switchToFrame(client, 'S1', 'iframe')).resolves.toEqual({ url: 'https://f.test/frame', sessionId: 'S-FRAME' });
|
|
149
|
+
const attach = calls.find((c) => c.method === 'Target.attachToTarget');
|
|
150
|
+
expect(attach.params).toEqual({ targetId: 'T-OOPIF', flatten: true });
|
|
151
|
+
});
|
|
152
|
+
it('returns the src with null sessionId when the frame tree has no match', async () => {
|
|
153
|
+
const { client, calls } = stubClient({
|
|
154
|
+
'Runtime.evaluate': { result: { result: { value: 'https://f.test/frame' } } },
|
|
155
|
+
'Page.getFrameTree': frameTreeWith('https://other.test/elsewhere'),
|
|
156
|
+
});
|
|
157
|
+
await expect(switchToFrame(client, 'S1', 'iframe')).resolves.toEqual({ url: 'https://f.test/frame', sessionId: null });
|
|
158
|
+
expect(calls.map((c) => c.method)).not.toContain('Target.getTargets');
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
//# sourceMappingURL=tabs.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tabs.test.js","sourceRoot":"","sources":["../../../src/__tests__/tabs.test.ts"],"names":[],"mappings":"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"}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { CdpClient } from './cdp.js';
|
|
2
2
|
import type { BrowserConfig, CdpTarget } from './types.js';
|
|
3
|
+
/** PID of the Chrome instance *this process* launched on `port`, if any. */
|
|
4
|
+
export declare function getLaunchedPid(port: number): number | undefined;
|
|
5
|
+
/** userDataDir of the Chrome instance *this process* launched on `port`, if any. */
|
|
6
|
+
export declare function getLaunchedUserDataDir(port: number): string | undefined;
|
|
3
7
|
export declare function isPortOpen(port: number): Promise<boolean>;
|
|
4
8
|
export declare function launchBrowser(config?: BrowserConfig): Promise<number>;
|
|
5
9
|
export declare function enableSessionDomains(client: CdpClient, sessionId: string): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../src/browser/browser.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,SAAS,EAAgC,MAAM,UAAU,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../src/browser/browser.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,SAAS,EAAgC,MAAM,UAAU,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAwB3D,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE/D;AAED,oFAAoF;AACpF,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEvE;AAuBD,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/D;AA2CD,wBAAsB,aAAa,CAAC,MAAM,GAAE,aAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAuD/E;AAqED,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAW9F;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CA0B3I;AAED;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkEjF;AAED,wBAAsB,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK9F;AAED,wBAAsB,WAAW,CAC/B,MAAM,EAAE,SAAS,EACjB,SAAS,EAAE,MAAM,EACjB,SAAS,GAAE,MAAM,GAAG,aAAa,GAAG,kBAA2B,EAC/D,OAAO,SAAS,GACf,OAAO,CAAC,IAAI,CAAC,CA4Bf;AAgED,wBAAsB,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMzF;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAM3F"}
|
|
@@ -7,16 +7,31 @@ import { CdpClient, fetchTargets, fetchNewTarget } from './cdp.js';
|
|
|
7
7
|
import { CHROME_EXECUTABLES } from './types.js';
|
|
8
8
|
import { enableConsoleCapture, setupConsoleCapture } from './console-log.js';
|
|
9
9
|
import { setupDialogAutoHandling } from './dialog.js';
|
|
10
|
+
import { loadActivePortInfo } from './ref-cache.js';
|
|
10
11
|
const DEFAULT_PORT = 9222;
|
|
11
12
|
const LAUNCH_TIMEOUT = 10_000;
|
|
12
13
|
const POLL_INTERVAL = 200;
|
|
13
14
|
const BROWSER_CLOSE_TIMEOUT_MS = 3000;
|
|
15
|
+
// A cross-process persisted PID older than this is not trusted for the
|
|
16
|
+
// SIGKILL fallback in closeBrowser() — see the call site for why.
|
|
17
|
+
const PERSISTED_PID_MAX_AGE_MS = 24 * 60 * 60 * 1000;
|
|
14
18
|
// Tracks the PID of Chrome instances *we* spawned, keyed by CDP port, so
|
|
15
19
|
// closeBrowser() has a kill fallback when the graceful `Browser.close` CDP
|
|
16
20
|
// command fails or times out (e.g. a hung renderer). Ports we merely attached
|
|
17
21
|
// to (already-running browser) are never recorded here — we only ever kill
|
|
18
22
|
// processes we launched ourselves.
|
|
19
23
|
const launchedPids = new Map();
|
|
24
|
+
// userDataDir per launched port — informational, persisted alongside the pid
|
|
25
|
+
// so a later process could clean up the temp profile dir too if ever needed.
|
|
26
|
+
const launchedUserDataDirs = new Map();
|
|
27
|
+
/** PID of the Chrome instance *this process* launched on `port`, if any. */
|
|
28
|
+
export function getLaunchedPid(port) {
|
|
29
|
+
return launchedPids.get(port);
|
|
30
|
+
}
|
|
31
|
+
/** userDataDir of the Chrome instance *this process* launched on `port`, if any. */
|
|
32
|
+
export function getLaunchedUserDataDir(port) {
|
|
33
|
+
return launchedUserDataDirs.get(port);
|
|
34
|
+
}
|
|
20
35
|
function findChrome(executablePath) {
|
|
21
36
|
if (executablePath) {
|
|
22
37
|
if (existsSync(executablePath))
|
|
@@ -174,9 +189,12 @@ async function launchOnFreePort(config, port) {
|
|
|
174
189
|
stdio: 'ignore',
|
|
175
190
|
});
|
|
176
191
|
child.unref();
|
|
177
|
-
if (child.pid)
|
|
192
|
+
if (child.pid) {
|
|
178
193
|
launchedPids.set(port, child.pid);
|
|
179
|
-
|
|
194
|
+
launchedUserDataDirs.set(port, userDataDir);
|
|
195
|
+
}
|
|
196
|
+
const launchTimeout = config.launchTimeoutMs ?? LAUNCH_TIMEOUT;
|
|
197
|
+
const deadline = Date.now() + launchTimeout;
|
|
180
198
|
while (Date.now() < deadline) {
|
|
181
199
|
await sleep(POLL_INTERVAL);
|
|
182
200
|
if (await isPortOpen(port)) {
|
|
@@ -190,10 +208,10 @@ async function launchOnFreePort(config, port) {
|
|
|
190
208
|
// "nothing is listening" (real launch failure) from "something non-CDP is
|
|
191
209
|
// squatting the port" (confusing generic timeout otherwise) via a raw TCP probe.
|
|
192
210
|
if (await isTcpPortOpen(port)) {
|
|
193
|
-
throw new Error(`Port ${port} is occupied by a non-Chrome process (TCP connection succeeds but no CDP response within ${
|
|
211
|
+
throw new Error(`Port ${port} is occupied by a non-Chrome process (TCP connection succeeds but no CDP response within ${launchTimeout}ms). ` +
|
|
194
212
|
`Free the port or pass a different one.`);
|
|
195
213
|
}
|
|
196
|
-
throw new Error(`Chrome failed to start on port ${port} within ${
|
|
214
|
+
throw new Error(`Chrome failed to start on port ${port} within ${launchTimeout}ms`);
|
|
197
215
|
}
|
|
198
216
|
export async function enableSessionDomains(client, sessionId) {
|
|
199
217
|
await Promise.all([
|
|
@@ -257,15 +275,45 @@ export async function closeBrowser(client, port) {
|
|
|
257
275
|
catch {
|
|
258
276
|
// fall through to PID-kill fallback below
|
|
259
277
|
}
|
|
260
|
-
|
|
278
|
+
let pid = launchedPids.get(port);
|
|
279
|
+
if (pid === undefined) {
|
|
280
|
+
// Fresh process (each CLI invocation is its own node process — see
|
|
281
|
+
// module header) — launchedPids is per-process and empty here even
|
|
282
|
+
// though a prior `open` in a DIFFERENT process launched this Chrome.
|
|
283
|
+
// Fall back to the PID that process persisted via saveActivePort().
|
|
284
|
+
try {
|
|
285
|
+
const persisted = await loadActivePortInfo();
|
|
286
|
+
if (persisted && persisted.port === port && persisted.launched && persisted.pid) {
|
|
287
|
+
// Bound how far we trust a cross-process PID: if the Chrome we
|
|
288
|
+
// launched died outside this flow (OOM-killed, host slept, etc.)
|
|
289
|
+
// without active-port.json ever being cleared, the OS can recycle
|
|
290
|
+
// that PID for an unrelated process. There is no cmdline/start-time
|
|
291
|
+
// check available portably, so a coarse age bound is the practical
|
|
292
|
+
// guard — a PID persisted more than a day ago is far more likely to
|
|
293
|
+
// be stale/reused than to still be the same live Chrome.
|
|
294
|
+
const age = persisted.savedAt !== undefined ? Date.now() - persisted.savedAt : undefined;
|
|
295
|
+
if (age === undefined || age <= PERSISTED_PID_MAX_AGE_MS) {
|
|
296
|
+
pid = persisted.pid;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
// best-effort — fall through to "nothing to kill" below
|
|
302
|
+
}
|
|
303
|
+
}
|
|
261
304
|
if (pid === undefined)
|
|
262
|
-
return; // not a process we launched — nothing to kill
|
|
305
|
+
return; // not a process we launched, or too stale to trust — nothing to kill
|
|
263
306
|
launchedPids.delete(port);
|
|
307
|
+
// Liveness-check before every SIGKILL (not just the safety-net path below)
|
|
308
|
+
// — process.kill(pid, 0) throws ESRCH if the PID is no longer running,
|
|
309
|
+
// which also catches the case where the PID has already been recycled by
|
|
310
|
+
// an unrelated process that happens to have exited too.
|
|
264
311
|
if (!gracefullyClosed) {
|
|
265
312
|
try {
|
|
313
|
+
process.kill(pid, 0);
|
|
266
314
|
process.kill(pid, 'SIGKILL');
|
|
267
315
|
}
|
|
268
|
-
catch { /* already exited */ }
|
|
316
|
+
catch { /* already exited, or never was — nothing to do */ }
|
|
269
317
|
return;
|
|
270
318
|
}
|
|
271
319
|
// Browser.close was acknowledged — give the process a moment to exit on
|