GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
scripts/web-probe/clipboard.mjs (3.9K)
1 // clipboard.mjs <site-dir>: the clipboard through the browser shell, in the
2 // demo's text box (src/main.rs on demo_web). Four checks, each read back
3 // from the system clipboard:
4 // 1. copy — Ctrl+A, Ctrl+C — puts the box's text there;
5 // 2. paste — Ctrl+V with other text there — takes it (copied back out);
6 // 3. copy with the page's writeText refused: the `copy` event alone
7 // carries it (what an insecure context has);
8 // 4. paste with the `paste` event swallowed but its default kept: the text
9 // the browser pastes into the keyboard sink is the clipboard's;
10 // 5. paste with the `paste` event swallowed and cancelled: the held key
11 // goes through on its timer and pastes the page's own last copy or
12 // paste (4's read-back copy of the box).
13 import { open } from './browser.mjs';
14
15 const [root] = process.argv.slice(2);
16 if (!root) { console.error('usage: clipboard.mjs <site-dir>'); process.exit(2); }
17 let ok = true;
18 const check = (what, got, want) => {
19 const pass = got === want;
20 ok &&= pass;
21 console.log(`${pass ? 'ok ' : 'FAIL'} ${what}: ${JSON.stringify(got)}${pass ? '' : ` (want ${JSON.stringify(want)})`}`);
22 };
23
24 async function session(body) {
25 const { page, logs, close } = await open(root, 'demo.html', 1280, 800);
26 await page.context().grantPermissions(['clipboard-read', 'clipboard-write']);
27 const m = page.mouse, k = page.keyboard, wait = ms => page.waitForTimeout(ms);
28 const t = {
29 page, wait,
30 read: () => page.evaluate(() => navigator.clipboard.readText()),
31 write: s => page.evaluate(s => (window.realWrite || (t => navigator.clipboard.writeText(t)))(s), s),
32 chord: async key => { await k.down('Control'); await k.press(key); await k.up('Control'); await wait(400); },
33 focusBox: async () => { await m.move(640, 168); await m.down(); await m.up(); await wait(300); },
34 type: async s => { await k.type(s); await wait(300); },
35 key: async s => { await k.press(s); await wait(100); },
36 };
37 try {
38 await m.move(1279, 799);
39 await page.waitForFunction(() => window.demoReady === true, null, { timeout: 120000 });
40 await wait(3000);
41 await body(t);
42 } catch (e) { ok = false; logs.push('[clipboard] ' + String(e)); }
43 for (const l of logs) if (!l.includes('WebGPU is experimental')) console.log(l);
44 await close();
45 }
46
47 await session(async t => {
48 await t.write('before');
49 await t.focusBox(); await t.type('hello web');
50 await t.chord('a'); await t.chord('c');
51 check('copy', await t.read(), 'hello web');
52 await t.write('from the system');
53 await t.chord('a'); await t.chord('v');
54 await t.chord('a'); await t.chord('c');
55 check('paste', await t.read(), 'from the system');
56 });
57
58 await session(async t => {
59 await t.write('before');
60 await t.page.evaluate(() => {
61 const real = Clipboard.prototype.writeText;
62 window.realWrite = s => real.call(navigator.clipboard, s);
63 Clipboard.prototype.writeText = () => Promise.reject(new Error('refused'));
64 });
65 await t.focusBox(); await t.type('via the event');
66 await t.chord('a'); await t.chord('c');
67 check('copy, the copy event alone', await t.read(), 'via the event');
68 // Ahead of the shell's listener: swallow the event, and with
69 // `cancelPaste` set cancel its default too.
70 await t.page.evaluate(() => window.addEventListener('paste', e => {
71 e.stopImmediatePropagation();
72 if (window.cancelPaste) e.preventDefault();
73 }, true));
74 await t.write(' and the system');
75 await t.focusBox(); await t.key('End'); await t.chord('v');
76 await t.chord('a'); await t.chord('c');
77 check('paste, the event swallowed', await t.read(), 'via the event and the system');
78 await t.page.evaluate(() => { window.cancelPaste = true; });
79 await t.write('system text the page never sees');
80 await t.focusBox(); await t.key('End'); await t.chord('v');
81 await t.chord('a'); await t.chord('c');
82 check('paste, no paste event at all', await t.read(), 'via the event and the systemvia the event and the system');
83 });
84
85 process.exit(ok ? 0 : 1);