-
Notifications
You must be signed in to change notification settings - Fork 12
/
puppeteer.worker.ts
128 lines (103 loc) · 3.5 KB
/
puppeteer.worker.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Make sure WS transport is loaded and in webpack's cache
import 'puppeteer-core/lib/esm/puppeteer/common/BrowserWebSocketTransport';
import { Browser, Page, CDPSession } from 'puppeteer-core/lib/esm/puppeteer/api-docs-entry';
import puppeteer from 'puppeteer-core/lib/esm/puppeteer/web';
import {
ProtocolCommands,
HostCommands,
Message,
WorkerCommands,
} from './types';
const protocolCommands = Object.keys(ProtocolCommands);
let browser: Browser | void;
let page: Page | void;
let client: CDPSession | void;
const sendParentMessage = (message: Message) => {
// @ts-ignore
self.postMessage(message);
};
// Override console so that messages show up in the browser's console
// Since we're in a webworker this won't disable console messages in
// the main app itself.
Object.keys(self.console).forEach((consoleMethod: keyof Console) => {
// @ts-ignore
self.console[consoleMethod] = (...args: SerializableOrJSHandle[]) => page && page.evaluate((consoleMethod: keyof Console, ...args) => {
// @ts-ignore
console[consoleMethod](...args);
}, consoleMethod, ...args);
});
const start = async(data: Message['data']) => {
const { browserWSEndpoint, quality = 100 } = data;
browser = await puppeteer.connect({ browserWSEndpoint })
.catch((error) => {
console.error(error);
return undefined;
});
if (!browser) {
sendParentMessage({
command: WorkerCommands.error,
data: `⚠️ Couldn't establish a connection "${browserWSEndpoint}". Is your browser running?`,
});
return self.close();
}
browser.once('disconnected', () => {
sendParentMessage({ command: WorkerCommands.browserClose, data: null });
closeWorker();
});
page = await browser.newPage();
client = (page as any)._client as CDPSession;
await client.send('Page.startScreencast', { format: 'jpeg', quality });
client.on('Page.screencastFrame', onScreencastFrame);
sendParentMessage({
command: WorkerCommands.startComplete,
data: {
targetId: (page as any)._target._targetId,
},
});
};
const onScreencastFrame = ({ data, sessionId }: { data: string; sessionId: number }) => {
if (client) {
client.send('Page.screencastFrameAck', { sessionId }).catch(() => {});
sendParentMessage({ command: WorkerCommands.screencastFrame, data });
}
};
const setViewport = (data: { width: number, height: number, deviceScaleFactor: number }) => page && page.setViewport(data);
const runCode = async ({ code }: Message['data']) => {
eval(code)({ page })
.then(async (res: any) => sendParentMessage({
command: WorkerCommands.runComplete,
data: {
url: (page as Page).url(),
payload: res,
},
}))
.catch((e: Error) => {
page && page.evaluate((err) => console.error(err), e.toString());
});
};
const closeWorker = async() => {
if (browser) browser.disconnect();
return self.close();
}
// Register Commands
self.addEventListener('message', async (message) => {
const { command, data } = message.data as Message;
if (command === HostCommands.start) {
return start(data);
}
if (command === HostCommands.run) {
return runCode(data);
}
if (command === HostCommands.setViewport) {
return setViewport(data);
}
if (command === HostCommands.close) {
return closeWorker();
}
if (protocolCommands.includes(command)) {
if (!client) return;
const protocolCommand = command as ProtocolCommands;
return client.send(protocolCommand, data);
}
console.debug(`Unknown worker command:`, message);
}, false);