buninu-browser 0.3.0 → 0.4.0
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/LICENSES/jsmdcui.txt +22 -0
- package/README.md +19 -4
- package/frontends/wayland/LICENSE-land +21 -0
- package/frontends/wayland/README.md +123 -0
- package/frontends/wayland/casty/LICENSE +21 -0
- package/frontends/wayland/casty/bookmarks.js +32 -0
- package/frontends/wayland/casty/click.js +96 -0
- package/frontends/wayland/casty/commands.js +61 -0
- package/frontends/wayland/casty/config.js +45 -0
- package/frontends/wayland/casty/download-directory.js +28 -0
- package/frontends/wayland/casty/download.js +49 -0
- package/frontends/wayland/casty/hints.js +334 -0
- package/frontends/wayland/casty/keys.js +56 -0
- package/frontends/wayland/casty/page-commands.js +80 -0
- package/frontends/wayland/casty/prompt-width.js +148 -0
- package/frontends/wayland/casty/urlbar.js +606 -0
- package/frontends/wayland/client.js +88 -0
- package/frontends/wayland/clipboard.js +80 -0
- package/frontends/wayland/index.js +116 -0
- package/frontends/wayland/input.js +56 -0
- package/frontends/wayland/protocol.js +55 -0
- package/frontends/wayland/protocols/wayland.xml +3248 -0
- package/frontends/wayland/protocols/xdg-shell.xml +1415 -0
- package/frontends/wayland/transport.js +120 -0
- package/frontends/wayland/urlbar.js +158 -0
- package/index.js +28 -1
- package/lib/clipboard-command.js +10 -0
- package/lib/clipboard-powershell.js +22 -0
- package/lib/clipboard.js +37 -2
- package/lib/document-types.js +11 -0
- package/lib/headless-shell.js +47 -8
- package/lib/layout/text-layout.js +17 -9
- package/lib/paint/display-list.js +5 -1
- package/lib/qml/README.md +971 -0
- package/lib/qml/builtins.js +700 -0
- package/lib/qml/canvas-dom.js +115 -0
- package/lib/qml/document.js +1176 -0
- package/lib/qml/format.js +9 -0
- package/lib/qml/host.js +324 -0
- package/lib/qml/images.js +199 -0
- package/lib/qml/launcher.js +132 -0
- package/lib/qml/loader.js +517 -0
- package/lib/qml/media-renderer.js +49 -0
- package/lib/qml/media.js +86 -0
- package/lib/qml/models.js +622 -0
- package/lib/qml/parser.js +381 -0
- package/lib/qml/qs +5 -0
- package/lib/qml/qscasty +3 -0
- package/lib/qml/qsheadless +3 -0
- package/lib/qml/qswayland +3 -0
- package/lib/qml/reactive.js +159 -0
- package/lib/qml/runtime.js +3381 -0
- package/lib/qml/shapes.js +243 -0
- package/lib/qml/text.js +109 -0
- package/lib/qml/textures.js +123 -0
- package/lib/qml/wayland-clipboard.js +32 -0
- package/lib/qml/worker-thread.js +70 -0
- package/lib/render-tree/index.js +16 -1
- package/lib/renderer/bindings.js +11 -0
- package/lib/renderer/canvas-2d.js +13 -0
- package/lib/renderer/host.js +31 -6
- package/lib/renderer/main.js +19 -6
- package/lib/renderer/page-realm.js +16 -1
- package/lib/renderer/page-renderer.js +230 -13
- package/lib/renderer/text-input-paint.js +54 -0
- package/lib/renderer/text-input.js +57 -0
- package/lib/style/computed-style.js +2 -2
- package/package.json +1 -1
- package/test/clipboard.test.js +19 -0
- package/test/fixtures/flea-helpers/Dockerfile +36 -0
- package/test/fixtures/flea-helpers/bat.mjs +43 -0
- package/test/fixtures/flea-helpers/bin/bat +3 -0
- package/test/fixtures/flea-helpers/bin/bwrap +10 -0
- package/test/fixtures/flea-helpers/bin/bwrap-real-filter +9 -0
- package/test/fixtures/flea-helpers/bin/glow +3 -0
- package/test/fixtures/flea-helpers/bin/qs +3 -0
- package/test/fixtures/flea-helpers/bin/unsupported +3 -0
- package/test/fixtures/flea-helpers/check-highlighting.mjs +47 -0
- package/test/fixtures/flea-helpers/check-media.mjs +29 -0
- package/test/fixtures/flea-helpers/check-preview.mjs +27 -0
- package/test/fixtures/flea-helpers/check.mjs +36 -0
- package/test/fixtures/flea-helpers/highlight-cases.json +227 -0
- package/test/fixtures/flea-helpers/source-mime.xml +28 -0
- package/test/fixtures/flea-helpers/syntax-compat.mjs +23 -0
- package/test/fixtures/flea-thumbnail-stub/flea-placeholder.thumbnailer +4 -0
- package/test/fixtures/flea-thumbnail-stub/flea-thumbnail-stub +3 -0
- package/test/fixtures/qml/flea/EscapeHint.qml +11 -0
- package/test/fixtures/qml/flea/LICENSE +21 -0
- package/test/fixtures/qml/flea/README.md +25 -0
- package/test/fixtures/qml/flea/Theme.qml +8 -0
- package/test/fixtures/qml/flea/demo.qml +24 -0
- package/test/fixtures/qml/flea/qmldir +3 -0
- package/test/fixtures/qml/flea-theme/LICENSE-Flea +21 -0
- package/test/fixtures/qml/flea-theme/LICENSE-Omarchy +20 -0
- package/test/fixtures/qml/flea-theme/README.md +70 -0
- package/test/fixtures/qml/flea-theme/button.qml +17 -0
- package/test/fixtures/qml/flea-theme/demo.qml +27 -0
- package/test/fixtures/qml/flea-theme/field.qml +21 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/BorderGeometry.js +373 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/Color.qml +258 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/I18n.qml +118 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/Numbers.js +19 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/Plural.js +91 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/Style.qml +561 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/Util.qml +188 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/qmldir +13 -0
- package/test/fixtures/qml/flea-theme/segment.qml +17 -0
- package/test/fixtures/qml/flea-theme/settings.qml +58 -0
- package/test/fixtures/qml/flea-theme/text-metrics.qml +10 -0
- package/test/fixtures/qml/flea-theme/ui/DialogButton.qml +150 -0
- package/test/fixtures/qml/flea-theme/ui/DialogField.qml +147 -0
- package/test/fixtures/qml/flea-theme/ui/EscapeHint.qml +11 -0
- package/test/fixtures/qml/flea-theme/ui/Glass.qml +114 -0
- package/test/fixtures/qml/flea-theme/ui/Glyph.qml +54 -0
- package/test/fixtures/qml/flea-theme/ui/GlyphSolid.qml +22 -0
- package/test/fixtures/qml/flea-theme/ui/Reveal.qml +54 -0
- package/test/fixtures/qml/flea-theme/ui/SettingsRuler.qml +85 -0
- package/test/fixtures/qml/flea-theme/ui/SettingsSegment.qml +85 -0
- package/test/fixtures/qml/flea-theme/ui/StableModel.qml +89 -0
- package/test/fixtures/qml/flea-theme/ui/Theme.qml +460 -0
- package/test/fixtures/qml/flea-theme/ui/ThemeColors.qml +43 -0
- package/test/fixtures/qml/flea-theme/ui/ViewState.qml +565 -0
- package/test/fixtures/qml/flea-theme/ui/js/Anchor.js +379 -0
- package/test/fixtures/qml/flea-theme/ui/js/AnchorHold.js +162 -0
- package/test/fixtures/qml/flea-theme/ui/js/Archive.js +157 -0
- package/test/fixtures/qml/flea-theme/ui/js/BackReturn.js +110 -0
- package/test/fixtures/qml/flea-theme/ui/js/Buttons.js +54 -0
- package/test/fixtures/qml/flea-theme/ui/js/ColumnMenu.js +111 -0
- package/test/fixtures/qml/flea-theme/ui/js/Columns.js +298 -0
- package/test/fixtures/qml/flea-theme/ui/js/Contrast.js +157 -0
- package/test/fixtures/qml/flea-theme/ui/js/Density.js +44 -0
- package/test/fixtures/qml/flea-theme/ui/js/DirSizes.js +129 -0
- package/test/fixtures/qml/flea-theme/ui/js/ExtThumbs.js +115 -0
- package/test/fixtures/qml/flea-theme/ui/js/Filter.js +284 -0
- package/test/fixtures/qml/flea-theme/ui/js/FolderSorts.js +108 -0
- package/test/fixtures/qml/flea-theme/ui/js/Format.js +305 -0
- package/test/fixtures/qml/flea-theme/ui/js/Icons.js +206 -0
- package/test/fixtures/qml/flea-theme/ui/js/Input.js +39 -0
- package/test/fixtures/qml/flea-theme/ui/js/KeyName.js +64 -0
- package/test/fixtures/qml/flea-theme/ui/js/Keymap.js +370 -0
- package/test/fixtures/qml/flea-theme/ui/js/Kinds.js +145 -0
- package/test/fixtures/qml/flea-theme/ui/js/Lists.js +11 -0
- package/test/fixtures/qml/flea-theme/ui/js/MakeDefault.js +186 -0
- package/test/fixtures/qml/flea-theme/ui/js/Match.js +27 -0
- package/test/fixtures/qml/flea-theme/ui/js/Material.js +109 -0
- package/test/fixtures/qml/flea-theme/ui/js/Menu.js +513 -0
- package/test/fixtures/qml/flea-theme/ui/js/Motion.js +16 -0
- package/test/fixtures/qml/flea-theme/ui/js/Mounts.js +320 -0
- package/test/fixtures/qml/flea-theme/ui/js/MouseNav.js +13 -0
- package/test/fixtures/qml/flea-theme/ui/js/Nav.js +328 -0
- package/test/fixtures/qml/flea-theme/ui/js/Palette.js +33 -0
- package/test/fixtures/qml/flea-theme/ui/js/Places.js +236 -0
- package/test/fixtures/qml/flea-theme/ui/js/Protocols.js +296 -0
- package/test/fixtures/qml/flea-theme/ui/js/RecentMode.js +154 -0
- package/test/fixtures/qml/flea-theme/ui/js/Search.js +396 -0
- package/test/fixtures/qml/flea-theme/ui/js/Settings.js +476 -0
- package/test/fixtures/qml/flea-theme/ui/js/SettingsAbout.js +99 -0
- package/test/fixtures/qml/flea-theme/ui/js/SettingsShelf.js +52 -0
- package/test/fixtures/qml/flea-theme/ui/js/Sort.js +127 -0
- package/test/fixtures/qml/flea-theme/ui/js/StateWords.js +42 -0
- package/test/fixtures/qml/flea-theme/ui/js/Swap.js +166 -0
- package/test/fixtures/qml/flea-theme/ui/js/TextSize.js +107 -0
- package/test/fixtures/qml/flea-theme/ui/js/Thumbs.js +158 -0
- package/test/fixtures/qml/flea-theme/ui/js/UiState.js +438 -0
- package/test/fixtures/qml/flea-theme/ui/js/Update.js +132 -0
- package/test/fixtures/qml/flea-theme/ui/js/UserDirs.js +56 -0
- package/test/fixtures/qml/flea-theme/ui/qmldir +227 -0
- package/test/fixtures/qml/hello.qml +26 -0
- package/test/fixtures/qml/imports/Demo/Toolkit/VersionLabel.qml +2 -0
- package/test/fixtures/qml/imports/Demo/Toolkit/VersionLabelNew.qml +2 -0
- package/test/fixtures/qml/imports/Demo/Toolkit/qmldir +3 -0
- package/test/fixtures/qml/imports/components/Badge.qml +15 -0
- package/test/fixtures/qml/imports/logic/base.js +2 -0
- package/test/fixtures/qml/imports/logic/counter.js +4 -0
- package/test/fixtures/qml/imports/logic/math.js +3 -0
- package/test/fixtures/qml/imports/logic/shared.js +3 -0
- package/test/fixtures/qml/imports/main.qml +21 -0
- package/test/fixtures/qml/imports/theme/Theme.qml +8 -0
- package/test/fixtures/qml/imports/theme/qmldir +2 -0
- package/test/fixtures/qml/multiply.qml +61 -0
- package/test/fixtures/qml-canvas-dom.js +90 -0
- package/test/fixtures/qml-media-audio.js +39 -0
- package/test/helpers/flea-sandbox.js +48 -0
- package/test/helpers/quickshell-fixture.js +125 -0
- package/test/html-clipboard-wayland.test.js +47 -0
- package/test/index.test.js +13 -0
- package/test/manual-casty-without-qml.js +87 -0
- package/test/manual-flea-application.js +49 -0
- package/test/manual-flea-binary.js +62 -0
- package/test/manual-flea-qml.js +9 -0
- package/test/manual-flea-settings.js +40 -0
- package/test/manual-flea-theme.js +40 -0
- package/test/manual-qml-removal.js +46 -0
- package/test/manual-without-qml.js +45 -0
- package/test/qml-canvas-dom.test.js +37 -0
- package/test/qml-clipboard-bridge.test.js +25 -0
- package/test/qml-clipboard-wayland.test.js +40 -0
- package/test/qml-connections-timing.test.js +73 -0
- package/test/qml-dynamic.test.js +401 -0
- package/test/qml-flea-app.test.js +146 -0
- package/test/qml-flea-backend.test.js +78 -0
- package/test/qml-flea-columns-backend.test.js +38 -0
- package/test/qml-flea-compile.test.js +78 -0
- package/test/qml-flea-launcher.test.js +88 -0
- package/test/qml-flea-markdown-backend.test.js +31 -0
- package/test/qml-flea-media.test.js +28 -0
- package/test/qml-flea-navigation-backend.test.js +35 -0
- package/test/qml-flea-navigation-timing.test.js +147 -0
- package/test/qml-flea-preview-backend.test.js +53 -0
- package/test/qml-flea-previews.test.js +163 -0
- package/test/qml-flea-rows.test.js +101 -0
- package/test/qml-flea-textures.test.js +59 -0
- package/test/qml-flea-view-switch.test.js +33 -0
- package/test/qml-flea-wayland.test.js +68 -0
- package/test/qml-flea.test.js +34 -0
- package/test/qml-host.test.js +238 -0
- package/test/qml-imports.test.js +102 -0
- package/test/qml-inherited-handlers.test.js +51 -0
- package/test/qml-input-selection.test.js +66 -0
- package/test/qml-interactions.test.js +309 -0
- package/test/qml-language.test.js +167 -0
- package/test/qml-launcher.test.js +54 -0
- package/test/qml-layout-text.test.js +205 -0
- package/test/qml-media.test.js +63 -0
- package/test/qml-persisted-delegates.test.js +45 -0
- package/test/qml-quickshell-controls.test.js +65 -0
- package/test/qml-quickshell-settings.test.js +62 -0
- package/test/qml-repeater.test.js +72 -0
- package/test/qml-style-updates.test.js +124 -0
- package/test/qml-textures.test.js +66 -0
- package/test/qml-views.test.js +332 -0
- package/test/qml-worker.test.js +69 -0
- package/test/qml.test.js +83 -0
- package/test/renderer-host.test.js +11 -0
- package/test/text-input-pointer.test.js +31 -0
- package/test/text-input-scroll.test.js +29 -0
- package/test/text-input.test.js +47 -0
- package/test/wayland-frontend.test.js +83 -0
- package/test/wayland-urlbar.test.js +38 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {test,expect} from 'bun:test';
|
|
2
|
+
import {RendererHost} from '../lib/renderer/host.js';
|
|
3
|
+
|
|
4
|
+
test('HTML controls share selection, clipboard and blinking; API reads require user activation',async()=>{
|
|
5
|
+
let clipboard='貼上!',reads=0;
|
|
6
|
+
const host=new RendererHost({clipboardRead:async()=>{reads++;return clipboard;},clipboardWrite:async text=>{clipboard=text;}});
|
|
7
|
+
const run=s=>host.call('evaluate',s);
|
|
8
|
+
const wait=async predicate=>{for(let i=0;i<60;i++){if(await predicate())return;await Bun.sleep(25);}throw Error('Timed out');};
|
|
9
|
+
try {
|
|
10
|
+
await host.start();await host.call('resize',400,200,1);
|
|
11
|
+
await host.call('loadDocument',{url:'https://input.test/',contentType:'text/html',source:`<input id="i" value="abc😀xyz" style="width:300px;height:30px"><textarea id="t">abcd\nxy\n12345</textarea><button id="b">read</button><script>document.getElementById('b').onclick=async()=>{try{window.result=await navigator.clipboard.readText()}catch(e){window.result=e.name}}</script>`});
|
|
12
|
+
await host.call('whenLoaded');
|
|
13
|
+
await run(`document.getElementById('i').focus()`);
|
|
14
|
+
const state=()=>run(`(()=>{let e=document.getElementById('i');return [e.selectionStart,e.selectionEnd,e.value]})()`);
|
|
15
|
+
await host.call('press','a',{modifiers:2});expect((await state()).slice(0,2)).toEqual([0,8]);
|
|
16
|
+
await host.call('press','ArrowRight');expect((await state()).slice(0,2)).toEqual([8,8]);
|
|
17
|
+
await host.call('press','ArrowLeft',{modifiers:8});expect((await state()).slice(0,2)).toEqual([7,8]);
|
|
18
|
+
await host.call('press','c',{modifiers:2});await wait(()=>clipboard==='z');
|
|
19
|
+
await host.call('press','x',{modifiers:2});await wait(async()=>(await state())[2]==='abc😀xy');
|
|
20
|
+
clipboard='漢';await host.call('press','v',{modifiers:2});await wait(async()=>(await state())[2]==='abc😀xy漢');
|
|
21
|
+
await run(`document.getElementById('i').onkeydown=e=>{if(e.ctrlKey)e.preventDefault()}`);
|
|
22
|
+
await host.call('press','a',{modifiers:2});expect((await state()).slice(0,2)).toEqual([8,8]);
|
|
23
|
+
await run(`document.getElementById('t').focus();document.getElementById('t').setSelectionRange(3,3)`);
|
|
24
|
+
await host.call('press','ArrowDown',{modifiers:8});
|
|
25
|
+
expect(await run(`[document.getElementById('t').selectionStart,document.getElementById('t').selectionEnd]`)).toEqual([3,7]);
|
|
26
|
+
await host.call('press','ArrowDown',{modifiers:8});
|
|
27
|
+
expect(await run(`[document.getElementById('t').selectionStart,document.getElementById('t').selectionEnd]`)).toEqual([3,11]);
|
|
28
|
+
await run(`window.result='';document.getElementById('b').click()`);await wait(async()=>(await run('window.result'))!=='');
|
|
29
|
+
// The prior user action has already been consumed by paste, but arrow presses
|
|
30
|
+
// are also user activation. Consume once, then a synthetic click must fail.
|
|
31
|
+
await run(`window.result='';document.getElementById('b').click()`);await wait(async()=>(await run('window.result'))!=='');
|
|
32
|
+
expect(await run('window.result')).toBe('NotAllowedError');
|
|
33
|
+
const b=await run(`document.getElementById('b').getBoundingClientRect().toJSON()`);
|
|
34
|
+
await host.call('click',b.x+b.width/2,b.y+b.height/2);
|
|
35
|
+
await wait(async()=>(await run('window.result'))==='漢');
|
|
36
|
+
expect(reads).toBeGreaterThan(1);
|
|
37
|
+
await run(`document.getElementById('i').onkeydown=null;document.getElementById('i').focus();document.getElementById('i').select();document.getElementById('i').onbeforeinput=e=>e.preventDefault()`);
|
|
38
|
+
clipboard='blocked';await host.call('press','v',{modifiers:2});await Bun.sleep(100);
|
|
39
|
+
expect((await state())[2]).toBe('abc😀xy漢');
|
|
40
|
+
await run(`document.getElementById('i').type='password'`);
|
|
41
|
+
await host.call('press','c',{modifiers:2});await Bun.sleep(100);expect(clipboard).toBe('blocked');
|
|
42
|
+
await run(`document.getElementById('i').type='text'`);
|
|
43
|
+
await run(`document.getElementById('i').focus();document.getElementById('i').setSelectionRange(1,1)`);
|
|
44
|
+
const on=await host.call('screenshot',{format:'png'});await Bun.sleep(550);
|
|
45
|
+
const off=await host.call('screenshot',{format:'png'});expect(off).not.toBe(on);
|
|
46
|
+
} finally {host.close();}
|
|
47
|
+
},30000);
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import {test,expect} from 'bun:test';
|
|
2
|
+
import {encodeRequest,decode,interfaces} from '../frontends/wayland/protocol.js';
|
|
3
|
+
import {parseKeymap,keysym} from '../frontends/wayland/input.js';
|
|
4
|
+
import {chooseFrontend} from '../index.js';
|
|
5
|
+
|
|
6
|
+
test('Wayland launcher remains opt-in',()=>{
|
|
7
|
+
expect(chooseFrontend(['--wayland','about:blank'])).toEqual({frontend:'wayland',args:['about:blank']});
|
|
8
|
+
expect(chooseFrontend(['about:blank']).frontend).toBe('casty');
|
|
9
|
+
});
|
|
10
|
+
test('registry bind encodes dynamic new_id and SHM fd stays out of payload',()=>{
|
|
11
|
+
const packet=encodeRequest(2,'wl_registry','bind',[7,'wl_compositor',4,3]);
|
|
12
|
+
expect(decode(interfaces.get('wl_registry').requests[0],packet.data,[])).toEqual([7,'wl_compositor',4,3]);
|
|
13
|
+
const pool=encodeRequest(4,'wl_shm','create_pool',[8,91,4096]);
|
|
14
|
+
expect(pool.fds).toEqual([91]);expect(pool.data.length).toBe(16);
|
|
15
|
+
expect(decode(interfaces.get('wl_shm').requests[0],pool.data,[91])).toEqual([8,91,4096]);
|
|
16
|
+
expect(decode(interfaces.get('wl_shm').requests[0],pool.data,[])).toBeNull();
|
|
17
|
+
});
|
|
18
|
+
test('bounded keymap subset understands named and Unicode keysyms',()=>{
|
|
19
|
+
const map=parseKeymap('<K30> = 38; key <K30> { type="ALPHABETIC", [ 0x61, 0x41 ] };');
|
|
20
|
+
expect(map.get(30).map(keysym)).toEqual(['a','A']);expect(keysym('0x01004e2d')).toBe('中');
|
|
21
|
+
expect(keysym('0xff0d')).toBe('Enter');expect(keysym('0xffe1')).toBeNull();expect(keysym('Left')).toBe('ArrowLeft');expect(keysym('dead_acute')).toBeNull();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Optional sibling integration; never installs prerequisites or touches a running compositor.
|
|
25
|
+
test.skipIf(process.env.BUNINU_TEST_WAYLAND!=='1')('Browser SHM, pointer, keyboard and resize in Land',async()=>{
|
|
26
|
+
const {mkdtempSync,rmSync}=await import('node:fs');
|
|
27
|
+
const {start}=await import('../../buninu-land/compositor.js');
|
|
28
|
+
const {launch}=await import('../frontends/wayland/index.js');
|
|
29
|
+
const dir=mkdtempSync('/tmp/buninu-wayland-test-'),old=process.env.XDG_RUNTIME_DIR;
|
|
30
|
+
process.env.XDG_RUNTIME_DIR=dir;let land,app;
|
|
31
|
+
const wait=async(fn)=>{for(let i=0;i<500;i++){if(await fn())return;await Bun.sleep(20);}throw Error('Timed out waiting for Wayland state');};
|
|
32
|
+
try{
|
|
33
|
+
land=await start(['--remote-debugging-port=0','--window-size=640,480']);
|
|
34
|
+
app=await launch({socket:dir+'/wayland-buninu',url:'data:text/html,'+encodeURIComponent('<title>Ready</title><body style="margin:0;background:rgb(255,0,255)"><input id="text" style="width:200px;height:60px" oninput="document.title=this.value"><button style="position:absolute;left:0;top:100px;width:200px;height:60px" onclick="document.title=\'Clicked\'">Click</button>')});
|
|
35
|
+
await wait(()=>land.compositor.wm.visible[0]?.surface.xdg.toplevel.title==='Ready');
|
|
36
|
+
const surface=()=>land.compositor.wm.renderSurfaces().find(s=>s.pixels);
|
|
37
|
+
await wait(()=>surface());expect(surface().pixels.width).toBe(app.window.width);
|
|
38
|
+
const click=async(x,y)=>{const o=land.compositor.wm.origin(surface());for(const type of ['mouseMoved','mousePressed','mouseReleased'])land.context.dispatchMouseEvent({type,x:o.x+x,y:o.y+y,button:'left'});};
|
|
39
|
+
await click(30,157);await wait(()=>land.compositor.wm.visible[0].surface.xdg.toplevel.title==='Clicked');
|
|
40
|
+
await click(30,57);land.context.type('hello');
|
|
41
|
+
await wait(()=>land.compositor.wm.visible[0].surface.xdg.toplevel.title==='hello');
|
|
42
|
+
// Editing browser chrome must not suspend page frames. Conversely, blinking
|
|
43
|
+
// chrome alone must reuse the last raw page frame rather than recapture it.
|
|
44
|
+
let captures=0,presents=0;
|
|
45
|
+
const screenshot=app.browser.context.screenshot.bind(app.browser.context);
|
|
46
|
+
app.browser.context.screenshot=(...args)=>{captures++;return screenshot(...args);};
|
|
47
|
+
const present=app.window.present.bind(app.window);
|
|
48
|
+
app.window.present=(...args)=>{presents++;return present(...args);};
|
|
49
|
+
await app.browser.context.evaluate('document.activeElement.blur()');
|
|
50
|
+
app.toolbar.open();
|
|
51
|
+
await Bun.sleep(800);
|
|
52
|
+
const idleCaptures=captures,idlePresents=presents;
|
|
53
|
+
await Bun.sleep(1100);
|
|
54
|
+
expect(captures).toBe(idleCaptures);
|
|
55
|
+
expect(presents).toBeGreaterThan(idlePresents);
|
|
56
|
+
await app.browser.context.evaluate(`globalThis.animationTick=0;globalThis.animationTimer=setInterval(()=>{document.body.style.background=++animationTick%2?'red':'blue'},80)`);
|
|
57
|
+
const animatedCaptures=captures,animatedPresents=presents;
|
|
58
|
+
await wait(()=>captures>=animatedCaptures+3&&presents>=animatedPresents+3);
|
|
59
|
+
expect(app.toolbar.editor.editing).toBe(true);
|
|
60
|
+
expect(Number(await app.browser.context.evaluate('animationTick'))).toBeGreaterThan(2);
|
|
61
|
+
await app.browser.context.evaluate('clearInterval(animationTimer)');
|
|
62
|
+
app.toolbar.editor.cancelEditing();
|
|
63
|
+
const before=app.window.width;land.context.resize(900,700,1);
|
|
64
|
+
await wait(()=>app.window.width!==before&&surface().pixels.width===app.window.width);
|
|
65
|
+
expect(JSON.parse(await app.browser.context.evaluate('JSON.stringify([innerWidth,innerHeight])'))).toEqual([app.window.width,app.window.height-32]);
|
|
66
|
+
for(const type of ['keyDown','keyUp'])land.context.dispatchKeyEvent({type,key:'w',code:'KeyW',modifiers:4});
|
|
67
|
+
await wait(()=>app.client.connection.closed&&land.compositor.wm.visible.length===0);
|
|
68
|
+
await app.done;
|
|
69
|
+
expect(app.window.closed).toBe(true);
|
|
70
|
+
}finally{app?.close();land?.close();if(old===undefined)delete process.env.XDG_RUNTIME_DIR;else process.env.XDG_RUNTIME_DIR=old;rmSync(dir,{recursive:true,force:true});}
|
|
71
|
+
},30000);
|
|
72
|
+
|
|
73
|
+
test('Wayland input queued around close never calls the disposed renderer',async()=>{
|
|
74
|
+
const {attachInput}=await import('../frontends/wayland/input.js');
|
|
75
|
+
let capabilities,pointer,calls=0,errors=0,reject;
|
|
76
|
+
const client={globals:new Map([['wl_seat',{}]]),connection:{closed:false},bind(_i,_v,fn){capabilities=fn;return 2;},object(iface,fn){if(iface==='wl_pointer')pointer=fn;return 3;},request(){}};
|
|
77
|
+
const window={surface:4,closed:false};
|
|
78
|
+
attachInput(client,window,{move(){calls++;return new Promise((_r,no)=>reject=no);}},()=>errors++);
|
|
79
|
+
capabilities('capabilities',[1]);pointer('enter',[1,4,0,0]);pointer('motion',[1,3,4]);
|
|
80
|
+
await Promise.resolve();expect(calls).toBe(1);
|
|
81
|
+
window.closed=true;reject(Error('Renderer closed'));pointer('motion',[2,4,5]);
|
|
82
|
+
await Bun.sleep(0);expect(calls).toBe(1);expect(errors).toBe(0);
|
|
83
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {test,expect} from 'bun:test';
|
|
2
|
+
import {WaylandUrlBar,WAYLAND_COMMANDS} from '../frontends/wayland/urlbar.js';
|
|
3
|
+
const history={list:()=>[],add(){}};
|
|
4
|
+
test('Wayland chrome retains Casty selection, insert-paste, mode switch and commands',async()=>{
|
|
5
|
+
let copied='',quit=false;const navigated=[];
|
|
6
|
+
const context={url:'https://example.com/',navigate:async url=>navigated.push(url)};
|
|
7
|
+
const bar=new WaylandUrlBar(context,{history,close:()=>quit=true,clipboard:{read:async()=>'/x',write:async text=>copied=text}});
|
|
8
|
+
try{
|
|
9
|
+
bar.updateURL(context.url);bar.open();
|
|
10
|
+
expect(bar.editor.selectedText).toBe(context.url);
|
|
11
|
+
await bar.press('ArrowRight');expect(bar.editor.selectedText).toBe('');
|
|
12
|
+
await bar.press('ArrowLeft',{modifiers:8});expect(bar.editor.selectedText).toBe('/');
|
|
13
|
+
await bar.press('c',{modifiers:2});expect(copied).toBe('/');
|
|
14
|
+
await bar.press('v',{modifiers:2});expect(bar.editor.text).toBe('https://example.com/x');
|
|
15
|
+
await bar.press('ArrowRight');await bar.press('v',{modifiers:2});
|
|
16
|
+
expect(bar.editor.text).toBe('https://example.com/x/x');
|
|
17
|
+
await bar.press('ArrowLeft',{modifiers:8});
|
|
18
|
+
await bar.press('c',{modifiers:1});expect(copied).toBe('x');
|
|
19
|
+
await bar.press('Tab');expect(bar.editor.mode).toBe('cmd');
|
|
20
|
+
await bar.press('Tab');expect(bar.editor.mode).toBe('url');
|
|
21
|
+
await bar.press('Escape');expect(bar.editor.editing).toBe(false);
|
|
22
|
+
await bar.command('open https://example.org/');expect(navigated).toEqual(['https://example.org/']);
|
|
23
|
+
expect(WAYLAND_COMMANDS.some(c=>c.name==='cdp')).toBe(false);
|
|
24
|
+
await bar.command('q');expect(quit).toBe(true);
|
|
25
|
+
}finally{bar.dispose();}
|
|
26
|
+
});
|
|
27
|
+
test('Toolbar raster cache and horizontal scrolling preserve page-independent rendering',async()=>{
|
|
28
|
+
const bar=await new WaylandUrlBar({},{history}).initialize();
|
|
29
|
+
try{
|
|
30
|
+
bar.updateURL('https://example.com/'+'long-path/'.repeat(20));
|
|
31
|
+
const pixels=bar.pixels(320);expect(pixels.length).toBe(320*32*4);expect(bar.pixels(320)).toBe(pixels);
|
|
32
|
+
bar.open();await bar.press('ArrowRight');bar.pixels(320);
|
|
33
|
+
expect(bar.editor._scrollX).toBeGreaterThan(0);
|
|
34
|
+
await bar.press('Home');bar.pixels(320);expect(bar.editor._scrollX).toBe(0);
|
|
35
|
+
bar.editor.pointerDown(5,1000);bar.editor.pointerMove(10);bar.editor.pointerUp();
|
|
36
|
+
expect(bar.editor.selectedText.length).toBeGreaterThan(0);
|
|
37
|
+
}finally{bar.dispose();}
|
|
38
|
+
});
|