buninu-browser 0.3.0 → 0.5.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.
Files changed (296) hide show
  1. package/LICENSES/jsmdcui.txt +22 -0
  2. package/README.md +40 -4
  3. package/frontends/wayland/LICENSE-land +21 -0
  4. package/frontends/wayland/README.md +152 -0
  5. package/frontends/wayland/casty/LICENSE +21 -0
  6. package/frontends/wayland/casty/bookmarks.js +32 -0
  7. package/frontends/wayland/casty/click.js +96 -0
  8. package/frontends/wayland/casty/commands.js +61 -0
  9. package/frontends/wayland/casty/config.js +45 -0
  10. package/frontends/wayland/casty/download-directory.js +28 -0
  11. package/frontends/wayland/casty/download.js +49 -0
  12. package/frontends/wayland/casty/hints.js +334 -0
  13. package/frontends/wayland/casty/keys.js +56 -0
  14. package/frontends/wayland/casty/page-commands.js +80 -0
  15. package/frontends/wayland/casty/prompt-width.js +148 -0
  16. package/frontends/wayland/casty/urlbar.js +606 -0
  17. package/frontends/wayland/client.js +88 -0
  18. package/frontends/wayland/clipboard.js +80 -0
  19. package/frontends/wayland/index.js +130 -0
  20. package/frontends/wayland/input.js +75 -0
  21. package/frontends/wayland/protocol.js +55 -0
  22. package/frontends/wayland/protocols/wayland.xml +3248 -0
  23. package/frontends/wayland/protocols/xdg-shell.xml +1415 -0
  24. package/frontends/wayland/transport.js +120 -0
  25. package/frontends/wayland/urlbar.js +162 -0
  26. package/index.js +28 -1
  27. package/lib/audio/shared-connection.js +31 -0
  28. package/lib/cdp-server.js +15 -2
  29. package/lib/clipboard-command.js +10 -0
  30. package/lib/clipboard-powershell.js +22 -0
  31. package/lib/clipboard.js +37 -2
  32. package/lib/document-types.js +11 -0
  33. package/lib/headless-shell.js +54 -12
  34. package/lib/layout/text-layout.js +17 -9
  35. package/lib/paint/display-list.js +6 -1
  36. package/lib/qml/README.md +1427 -0
  37. package/lib/qml/builtins.js +771 -0
  38. package/lib/qml/canvas-dom.js +115 -0
  39. package/lib/qml/canvas.js +76 -0
  40. package/lib/qml/document.js +1199 -0
  41. package/lib/qml/fonts.js +69 -0
  42. package/lib/qml/format.js +9 -0
  43. package/lib/qml/host.js +352 -0
  44. package/lib/qml/images.js +199 -0
  45. package/lib/qml/launcher.js +132 -0
  46. package/lib/qml/loader.js +521 -0
  47. package/lib/qml/media-renderer.js +49 -0
  48. package/lib/qml/media.js +86 -0
  49. package/lib/qml/models.js +622 -0
  50. package/lib/qml/parser.js +381 -0
  51. package/lib/qml/qs +5 -0
  52. package/lib/qml/qscasty +3 -0
  53. package/lib/qml/qsheadless +3 -0
  54. package/lib/qml/qswayland +3 -0
  55. package/lib/qml/reactive.js +182 -0
  56. package/lib/qml/runtime.js +3603 -0
  57. package/lib/qml/shader/fragment.js +83 -0
  58. package/lib/qml/shader/qsb.js +27 -0
  59. package/lib/qml/shader/renderer.js +39 -0
  60. package/lib/qml/shader/runtime.js +61 -0
  61. package/lib/qml/shapes.js +251 -0
  62. package/lib/qml/shortcuts.js +65 -0
  63. package/lib/qml/text.js +109 -0
  64. package/lib/qml/textures.js +126 -0
  65. package/lib/qml/wayland-clipboard.js +32 -0
  66. package/lib/qml/window-constraints.js +16 -0
  67. package/lib/qml/worker-thread.js +70 -0
  68. package/lib/render-tree/index.js +16 -1
  69. package/lib/renderer/bindings.js +16 -1
  70. package/lib/renderer/canvas-2d.js +29 -0
  71. package/lib/renderer/document-selection.js +19 -0
  72. package/lib/renderer/host.js +45 -17
  73. package/lib/renderer/main.js +21 -6
  74. package/lib/renderer/page-realm.js +20 -10
  75. package/lib/renderer/page-renderer.js +497 -42
  76. package/lib/renderer/pointer-move.js +5 -3
  77. package/lib/renderer/render-interval.js +9 -0
  78. package/lib/renderer/text-input-paint.js +54 -0
  79. package/lib/renderer/text-input.js +57 -0
  80. package/lib/style/computed-style.js +10 -4
  81. package/package.json +1 -1
  82. package/test/audio-connection-lifetime.test.js +46 -0
  83. package/test/canvas-paint.test.js +8 -0
  84. package/test/clipboard.test.js +19 -0
  85. package/test/computed-style.test.js +12 -0
  86. package/test/document-selection.test.js +27 -0
  87. package/test/double-click.test.js +85 -0
  88. package/test/fixtures/canvas-damage.js +40 -0
  89. package/test/fixtures/fake-renderer.js +1 -0
  90. package/test/fixtures/flea-helpers/Dockerfile +36 -0
  91. package/test/fixtures/flea-helpers/bat.mjs +43 -0
  92. package/test/fixtures/flea-helpers/bin/bat +3 -0
  93. package/test/fixtures/flea-helpers/bin/bwrap +10 -0
  94. package/test/fixtures/flea-helpers/bin/bwrap-real-filter +9 -0
  95. package/test/fixtures/flea-helpers/bin/glow +3 -0
  96. package/test/fixtures/flea-helpers/bin/qs +3 -0
  97. package/test/fixtures/flea-helpers/bin/unsupported +3 -0
  98. package/test/fixtures/flea-helpers/check-highlighting.mjs +47 -0
  99. package/test/fixtures/flea-helpers/check-media.mjs +29 -0
  100. package/test/fixtures/flea-helpers/check-preview.mjs +27 -0
  101. package/test/fixtures/flea-helpers/check.mjs +36 -0
  102. package/test/fixtures/flea-helpers/highlight-cases.json +227 -0
  103. package/test/fixtures/flea-helpers/source-mime.xml +28 -0
  104. package/test/fixtures/flea-helpers/syntax-compat.mjs +23 -0
  105. package/test/fixtures/flea-thumbnail-stub/flea-placeholder.thumbnailer +4 -0
  106. package/test/fixtures/flea-thumbnail-stub/flea-thumbnail-stub +3 -0
  107. package/test/fixtures/omastorm-helpers/Dockerfile +35 -0
  108. package/test/fixtures/omastorm-helpers/check.mjs +56 -0
  109. package/test/fixtures/omastorm-helpers/launch.mjs +24 -0
  110. package/test/fixtures/omastorm-helpers/prepare.mjs +17 -0
  111. package/test/fixtures/omastorm-helpers/runtime.mjs +32 -0
  112. package/test/fixtures/pointer-paint-batching.js +23 -0
  113. package/test/fixtures/qml/flea/EscapeHint.qml +11 -0
  114. package/test/fixtures/qml/flea/LICENSE +21 -0
  115. package/test/fixtures/qml/flea/README.md +25 -0
  116. package/test/fixtures/qml/flea/Theme.qml +8 -0
  117. package/test/fixtures/qml/flea/demo.qml +24 -0
  118. package/test/fixtures/qml/flea/qmldir +3 -0
  119. package/test/fixtures/qml/flea-theme/LICENSE-Flea +21 -0
  120. package/test/fixtures/qml/flea-theme/LICENSE-Omarchy +20 -0
  121. package/test/fixtures/qml/flea-theme/README.md +70 -0
  122. package/test/fixtures/qml/flea-theme/button.qml +17 -0
  123. package/test/fixtures/qml/flea-theme/demo.qml +27 -0
  124. package/test/fixtures/qml/flea-theme/field.qml +21 -0
  125. package/test/fixtures/qml/flea-theme/imports/qs/Commons/BorderGeometry.js +373 -0
  126. package/test/fixtures/qml/flea-theme/imports/qs/Commons/Color.qml +258 -0
  127. package/test/fixtures/qml/flea-theme/imports/qs/Commons/I18n.qml +118 -0
  128. package/test/fixtures/qml/flea-theme/imports/qs/Commons/Numbers.js +19 -0
  129. package/test/fixtures/qml/flea-theme/imports/qs/Commons/Plural.js +91 -0
  130. package/test/fixtures/qml/flea-theme/imports/qs/Commons/Style.qml +561 -0
  131. package/test/fixtures/qml/flea-theme/imports/qs/Commons/Util.qml +188 -0
  132. package/test/fixtures/qml/flea-theme/imports/qs/Commons/qmldir +13 -0
  133. package/test/fixtures/qml/flea-theme/segment.qml +17 -0
  134. package/test/fixtures/qml/flea-theme/settings.qml +58 -0
  135. package/test/fixtures/qml/flea-theme/text-metrics.qml +10 -0
  136. package/test/fixtures/qml/flea-theme/ui/DialogButton.qml +150 -0
  137. package/test/fixtures/qml/flea-theme/ui/DialogField.qml +147 -0
  138. package/test/fixtures/qml/flea-theme/ui/EscapeHint.qml +11 -0
  139. package/test/fixtures/qml/flea-theme/ui/Glass.qml +114 -0
  140. package/test/fixtures/qml/flea-theme/ui/Glyph.qml +54 -0
  141. package/test/fixtures/qml/flea-theme/ui/GlyphSolid.qml +22 -0
  142. package/test/fixtures/qml/flea-theme/ui/Reveal.qml +54 -0
  143. package/test/fixtures/qml/flea-theme/ui/SettingsRuler.qml +85 -0
  144. package/test/fixtures/qml/flea-theme/ui/SettingsSegment.qml +85 -0
  145. package/test/fixtures/qml/flea-theme/ui/StableModel.qml +89 -0
  146. package/test/fixtures/qml/flea-theme/ui/Theme.qml +460 -0
  147. package/test/fixtures/qml/flea-theme/ui/ThemeColors.qml +43 -0
  148. package/test/fixtures/qml/flea-theme/ui/ViewState.qml +565 -0
  149. package/test/fixtures/qml/flea-theme/ui/js/Anchor.js +379 -0
  150. package/test/fixtures/qml/flea-theme/ui/js/AnchorHold.js +162 -0
  151. package/test/fixtures/qml/flea-theme/ui/js/Archive.js +157 -0
  152. package/test/fixtures/qml/flea-theme/ui/js/BackReturn.js +110 -0
  153. package/test/fixtures/qml/flea-theme/ui/js/Buttons.js +54 -0
  154. package/test/fixtures/qml/flea-theme/ui/js/ColumnMenu.js +111 -0
  155. package/test/fixtures/qml/flea-theme/ui/js/Columns.js +298 -0
  156. package/test/fixtures/qml/flea-theme/ui/js/Contrast.js +157 -0
  157. package/test/fixtures/qml/flea-theme/ui/js/Density.js +44 -0
  158. package/test/fixtures/qml/flea-theme/ui/js/DirSizes.js +129 -0
  159. package/test/fixtures/qml/flea-theme/ui/js/ExtThumbs.js +115 -0
  160. package/test/fixtures/qml/flea-theme/ui/js/Filter.js +284 -0
  161. package/test/fixtures/qml/flea-theme/ui/js/FolderSorts.js +108 -0
  162. package/test/fixtures/qml/flea-theme/ui/js/Format.js +305 -0
  163. package/test/fixtures/qml/flea-theme/ui/js/Icons.js +206 -0
  164. package/test/fixtures/qml/flea-theme/ui/js/Input.js +39 -0
  165. package/test/fixtures/qml/flea-theme/ui/js/KeyName.js +64 -0
  166. package/test/fixtures/qml/flea-theme/ui/js/Keymap.js +370 -0
  167. package/test/fixtures/qml/flea-theme/ui/js/Kinds.js +145 -0
  168. package/test/fixtures/qml/flea-theme/ui/js/Lists.js +11 -0
  169. package/test/fixtures/qml/flea-theme/ui/js/MakeDefault.js +186 -0
  170. package/test/fixtures/qml/flea-theme/ui/js/Match.js +27 -0
  171. package/test/fixtures/qml/flea-theme/ui/js/Material.js +109 -0
  172. package/test/fixtures/qml/flea-theme/ui/js/Menu.js +513 -0
  173. package/test/fixtures/qml/flea-theme/ui/js/Motion.js +16 -0
  174. package/test/fixtures/qml/flea-theme/ui/js/Mounts.js +320 -0
  175. package/test/fixtures/qml/flea-theme/ui/js/MouseNav.js +13 -0
  176. package/test/fixtures/qml/flea-theme/ui/js/Nav.js +328 -0
  177. package/test/fixtures/qml/flea-theme/ui/js/Palette.js +33 -0
  178. package/test/fixtures/qml/flea-theme/ui/js/Places.js +236 -0
  179. package/test/fixtures/qml/flea-theme/ui/js/Protocols.js +296 -0
  180. package/test/fixtures/qml/flea-theme/ui/js/RecentMode.js +154 -0
  181. package/test/fixtures/qml/flea-theme/ui/js/Search.js +396 -0
  182. package/test/fixtures/qml/flea-theme/ui/js/Settings.js +476 -0
  183. package/test/fixtures/qml/flea-theme/ui/js/SettingsAbout.js +99 -0
  184. package/test/fixtures/qml/flea-theme/ui/js/SettingsShelf.js +52 -0
  185. package/test/fixtures/qml/flea-theme/ui/js/Sort.js +127 -0
  186. package/test/fixtures/qml/flea-theme/ui/js/StateWords.js +42 -0
  187. package/test/fixtures/qml/flea-theme/ui/js/Swap.js +166 -0
  188. package/test/fixtures/qml/flea-theme/ui/js/TextSize.js +107 -0
  189. package/test/fixtures/qml/flea-theme/ui/js/Thumbs.js +158 -0
  190. package/test/fixtures/qml/flea-theme/ui/js/UiState.js +438 -0
  191. package/test/fixtures/qml/flea-theme/ui/js/Update.js +132 -0
  192. package/test/fixtures/qml/flea-theme/ui/js/UserDirs.js +56 -0
  193. package/test/fixtures/qml/flea-theme/ui/qmldir +227 -0
  194. package/test/fixtures/qml/hello.qml +26 -0
  195. package/test/fixtures/qml/imports/Demo/Toolkit/VersionLabel.qml +2 -0
  196. package/test/fixtures/qml/imports/Demo/Toolkit/VersionLabelNew.qml +2 -0
  197. package/test/fixtures/qml/imports/Demo/Toolkit/qmldir +3 -0
  198. package/test/fixtures/qml/imports/components/Badge.qml +15 -0
  199. package/test/fixtures/qml/imports/logic/base.js +2 -0
  200. package/test/fixtures/qml/imports/logic/counter.js +4 -0
  201. package/test/fixtures/qml/imports/logic/math.js +3 -0
  202. package/test/fixtures/qml/imports/logic/shared.js +3 -0
  203. package/test/fixtures/qml/imports/main.qml +21 -0
  204. package/test/fixtures/qml/imports/theme/Theme.qml +8 -0
  205. package/test/fixtures/qml/imports/theme/qmldir +2 -0
  206. package/test/fixtures/qml/multiply.qml +61 -0
  207. package/test/fixtures/qml-canvas-dom.js +90 -0
  208. package/test/fixtures/qml-fonts/main.qml +10 -0
  209. package/test/fixtures/qml-locale/check.mjs +6 -0
  210. package/test/fixtures/qml-locale/main.qml +8 -0
  211. package/test/fixtures/qml-media-audio.js +39 -0
  212. package/test/fixtures/qml-shader/grab.qml +14 -0
  213. package/test/fixtures/qml-shader/gradient.frag +5 -0
  214. package/test/fixtures/qml-shader/main.qml +14 -0
  215. package/test/fixtures/qml-shader/texture-grab.qml +25 -0
  216. package/test/fixtures/qml-shader/texture-size.frag +5 -0
  217. package/test/fixtures/qml-shader-check.mjs +24 -0
  218. package/test/fixtures/title-no-paint.js +20 -0
  219. package/test/helpers/flea-sandbox.js +48 -0
  220. package/test/helpers/quickshell-fixture.js +125 -0
  221. package/test/html-clipboard-wayland.test.js +47 -0
  222. package/test/index.test.js +13 -0
  223. package/test/manual-casty-without-qml.js +87 -0
  224. package/test/manual-double-click.html +21 -0
  225. package/test/manual-flea-application.js +49 -0
  226. package/test/manual-flea-binary.js +62 -0
  227. package/test/manual-flea-qml.js +9 -0
  228. package/test/manual-flea-settings.js +40 -0
  229. package/test/manual-flea-theme.js +40 -0
  230. package/test/manual-mouse-buttons.html +18 -0
  231. package/test/manual-omastorm-engine.mjs +19 -0
  232. package/test/manual-omastorm-shaders.mjs +28 -0
  233. package/test/manual-omastorm-software.mjs +25 -0
  234. package/test/manual-pointer-scroll.html +43 -0
  235. package/test/manual-qml-removal.js +46 -0
  236. package/test/manual-without-qml.js +45 -0
  237. package/test/pointer-drag.test.js +104 -0
  238. package/test/qml-canvas-dom.test.js +37 -0
  239. package/test/qml-canvas.test.js +21 -0
  240. package/test/qml-clipboard-bridge.test.js +25 -0
  241. package/test/qml-clipboard-wayland.test.js +40 -0
  242. package/test/qml-connections-timing.test.js +95 -0
  243. package/test/qml-controls-window.test.js +182 -0
  244. package/test/qml-dynamic.test.js +401 -0
  245. package/test/qml-flea-app.test.js +146 -0
  246. package/test/qml-flea-backend.test.js +78 -0
  247. package/test/qml-flea-columns-backend.test.js +38 -0
  248. package/test/qml-flea-compile.test.js +78 -0
  249. package/test/qml-flea-launcher.test.js +88 -0
  250. package/test/qml-flea-markdown-backend.test.js +31 -0
  251. package/test/qml-flea-media.test.js +28 -0
  252. package/test/qml-flea-navigation-backend.test.js +35 -0
  253. package/test/qml-flea-navigation-timing.test.js +147 -0
  254. package/test/qml-flea-preview-backend.test.js +53 -0
  255. package/test/qml-flea-previews.test.js +163 -0
  256. package/test/qml-flea-rows.test.js +101 -0
  257. package/test/qml-flea-textures.test.js +59 -0
  258. package/test/qml-flea-view-switch.test.js +33 -0
  259. package/test/qml-flea-wayland.test.js +68 -0
  260. package/test/qml-flea.test.js +34 -0
  261. package/test/qml-fonts.test.js +31 -0
  262. package/test/qml-grid-multiline.test.js +48 -0
  263. package/test/qml-host.test.js +238 -0
  264. package/test/qml-imports.test.js +102 -0
  265. package/test/qml-inherited-handlers.test.js +51 -0
  266. package/test/qml-input-selection.test.js +66 -0
  267. package/test/qml-interactions.test.js +309 -0
  268. package/test/qml-language.test.js +167 -0
  269. package/test/qml-launcher.test.js +54 -0
  270. package/test/qml-layout-text.test.js +205 -0
  271. package/test/qml-locale.test.js +45 -0
  272. package/test/qml-media.test.js +63 -0
  273. package/test/qml-nonvisual-visible.test.js +19 -0
  274. package/test/qml-persisted-delegates.test.js +45 -0
  275. package/test/qml-quickshell-controls.test.js +65 -0
  276. package/test/qml-quickshell-settings.test.js +62 -0
  277. package/test/qml-repeater.test.js +72 -0
  278. package/test/qml-shader-runtime.test.js +6 -0
  279. package/test/qml-shortcuts.test.js +39 -0
  280. package/test/qml-socket.test.js +60 -0
  281. package/test/qml-software-shader.test.js +55 -0
  282. package/test/qml-style-updates.test.js +124 -0
  283. package/test/qml-textures.test.js +66 -0
  284. package/test/qml-views.test.js +332 -0
  285. package/test/qml-window-update.test.js +36 -0
  286. package/test/qml-worker.test.js +69 -0
  287. package/test/qml.test.js +83 -0
  288. package/test/render-interval.test.js +33 -0
  289. package/test/renderer-host.test.js +11 -0
  290. package/test/text-input-pointer.test.js +31 -0
  291. package/test/text-input-scroll.test.js +29 -0
  292. package/test/text-input.test.js +47 -0
  293. package/test/title-no-paint.test.js +6 -0
  294. package/test/wayland-frontend.test.js +83 -0
  295. package/test/wayland-scroll.test.js +40 -0
  296. package/test/wayland-urlbar.test.js +61 -0
@@ -0,0 +1,115 @@
1
+ /** Local-QML-only drawImage(Element) adapter. No DOM mutation or encoded images. */
2
+ import { buildDisplayList } from '../paint/display-list.js';
3
+
4
+ const documents = new WeakSet();
5
+ const snapshots = new WeakMap();
6
+ export function enableQmlDomImages(document) { documents.add(document); }
7
+ export function hasQmlDomImages(document) { return documents.has(document); }
8
+ export function qmlTextureVisibilityChanged(records) {
9
+ return records.some(record => record.type === 'attributes' && record.attributeName === 'data-qml-source-hidden');
10
+ }
11
+
12
+ /** Suppress only main-scene painting; layout and hit testing remain intact. */
13
+ export function filterQmlTextureSources(list, tree, captureRoot = null) {
14
+ const hidden = new Set();
15
+ function visit(node, suppressed = false) {
16
+ suppressed ||= node.domNode !== captureRoot && node.domNode?.hasAttribute?.('data-qml-source-hidden');
17
+ if (suppressed) hidden.add(node.id);
18
+ for (const child of node.children ?? []) visit(child, suppressed);
19
+ }
20
+ visit(tree.root);
21
+ if (!hidden.size) return list;
22
+ // The original list is frozen; create fresh descriptors for changed items.
23
+ const descriptors = Object.getOwnPropertyDescriptors(list);
24
+ descriptors.items = { value: list.items.filter(item => !hidden.has(item.nodeId)), enumerable: true };
25
+ return Object.create(Object.getPrototypeOf(list), descriptors);
26
+ }
27
+
28
+ /** Extract laid-out content, retaining internal stacking/clips but not ancestors. */
29
+ export function subtreeDisplayList(element, { tree, layout, resources, backgroundResource, scrollOffsetOf }) {
30
+ let root = [...tree.nodesById.values()].find(node => node.domNode === element);
31
+ const box = root && layout.boxes.find(box => box.nodeId === root.id && !box.inline);
32
+ if (!root || !box) throw new DOMException('DOM image source needs a connected, laid-out box.', 'InvalidStateError');
33
+ // Qt texture roots are captured opaque and without their placement transform.
34
+ if (element.hasAttribute?.('data-qml-texture-source'))
35
+ root = { ...root, style: { ...root.style, opacity: 1, transform: null, visibility: 'visible' } };
36
+ const nodesById = new Map();
37
+ const visit = node => { nodesById.set(node.id, node); for (const child of node.children ?? []) visit(child); };
38
+ visit(root);
39
+ // A synthetic parent keeps the source's own overflow clip, which the painter
40
+ // ordinarily skips for its document root. Ancestor effects are not imported.
41
+ const isolatedTree = { root: { id: Symbol('qml-image-root'), children: [root] }, nodesById };
42
+ const boxes = layout.boxes.filter(entry => nodesById.has(entry.nodeId));
43
+ const fragments = layout.fragments.map(fragment => ({ ...fragment,
44
+ runs: fragment.runs.filter(run => nodesById.has(run.nodeId)),
45
+ })).filter(fragment => fragment.runs.length);
46
+ // A selected layer can have an ancestor stacking context with no selected
47
+ // boxes. Keep that context's ordering metadata, never its painted content.
48
+ const layers = new Set();
49
+ for (const entry of [...boxes, ...fragments]) {
50
+ for (let layer = entry.layer; layer && layer.order !== 0; layer = layer.parent) layers.add(layer);
51
+ }
52
+ for (const layer of layers) fragments.push({ layer, runs: [] });
53
+ let list = buildDisplayList({ ...layout, boxes, fragments }, isolatedTree,
54
+ resources, backgroundResource, scrollOffsetOf);
55
+ // Snapshot coordinates do not scroll with the page viewport. Fixed items
56
+ // already carry their laid-out positions and must not receive its offset.
57
+ if (list.items.some(item => item.fixed)) {
58
+ const images = list.images;
59
+ list = { ...list, items: list.items.map(item => item.fixed ? { ...item, fixed: false } : item) };
60
+ Object.defineProperty(list, 'images', { value: images });
61
+ }
62
+ list = filterQmlTextureSources(list, isolatedTree, element);
63
+ return { list, box, nodesById };
64
+ }
65
+
66
+ export function captureQmlDomImage(element, options) {
67
+ const { document, kit, resources, canvasResource, isOriginClean, paint } = options;
68
+ if (element?.nodeType !== 1 || element.ownerDocument !== document || !element.isConnected) {
69
+ throw new DOMException('DOM image source must belong to this QML document.', 'InvalidStateError');
70
+ }
71
+ // Refresh only nested canvas resources; no whole-page raster is needed.
72
+ for (const node of options.tree.nodesById.values()) {
73
+ if (node.tagName !== 'CANVAS' || !element.contains(node.domNode)) continue;
74
+ const bitmap = canvasResource(node.domNode);
75
+ if (bitmap) resources.set(node.domNode, bitmap);
76
+ else resources.delete(node.domNode);
77
+ }
78
+ const { list, box, nodesById } = subtreeDisplayList(element, options);
79
+ const width = Math.ceil(box.width), height = Math.ceil(box.height);
80
+ if (!(width > 0 && height > 0) || width > 8192 || height > 8192 || width * height > 16777216) {
81
+ throw new DOMException('DOM image dimensions exceed the snapshot limit.', 'InvalidStateError');
82
+ }
83
+ // Frame resources are controlled by another document. This internal feature
84
+ // deliberately does not capture frames or browser-owned UI.
85
+ if ([...nodesById.values()].some(node => node.domNode?.tagName === 'IFRAME')) {
86
+ throw new DOMException('DOM images cannot contain frames.', 'NotSupportedError');
87
+ }
88
+ const originClean = list.images.every(isOriginClean);
89
+ const surface = kit.MakeSurface(width, height);
90
+ if (!surface) throw new DOMException('Cannot allocate DOM image.', 'InvalidStateError');
91
+ try {
92
+ surface.getCanvas().clear(kit.TRANSPARENT);
93
+ paint(surface.getCanvas(), list, { x: box.x, y: box.y, width, height }, 1);
94
+ surface.flush();
95
+ const image = surface.makeImageSnapshot();
96
+ if (!image) throw new DOMException('Cannot snapshot DOM image.', 'InvalidStateError');
97
+ const resource = { image, width, height, originClean };
98
+ snapshots.set(resource, { element, image });
99
+ return resource;
100
+ } finally { surface.dispose(); }
101
+ }
102
+
103
+ /** Consume ephemeral sources even if drawImage rejects its arguments. */
104
+ export function drawQmlDomImage(resource, target, context, args) {
105
+ const snapshot = resource && snapshots.get(resource);
106
+ if (!snapshot) return false;
107
+ snapshots.delete(resource);
108
+ try {
109
+ if (snapshot.element.contains(target)) {
110
+ throw new DOMException('DOM image source contains the destination canvas.', 'InvalidStateError');
111
+ }
112
+ context.drawImage(snapshot.image, ...args.slice(1));
113
+ return true;
114
+ } finally { snapshot.image.delete(); }
115
+ }
@@ -0,0 +1,76 @@
1
+ // QML lifecycle adapter; all pixels and drawing commands use HTML Canvas 2D.
2
+ export function createQmlCanvas(api) {
3
+ const {effect, untrack, emit, cleanup} = api;
4
+ function make(v, type, element) {
5
+ if (type !== 'Canvas') return;
6
+ const canvas = document.createElement('canvas');
7
+ canvas.style.cssText = 'position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none';
8
+ element.appendChild(canvas);
9
+ let frame, disposed = false, ready = false;
10
+ const callbacks = new Set();
11
+ Object.assign(v, {available:false, contextType:'2d', canvasSize:{width:0,height:0}});
12
+ const images=new Map();
13
+ v.loadImage=url=>{
14
+ url=String(url);if(images.has(url))return;
15
+ const image=document.createElement('img'),entry={image,status:'loading'};
16
+ images.set(url,entry);
17
+ entry.loaded=()=>{if(disposed||images.get(url)!==entry||!image.naturalWidth||!image.naturalHeight)return;entry.status='loaded';emit(v,'onImageLoaded');};
18
+ image.addEventListener('load',entry.loaded);
19
+ entry.failed=()=>{if(disposed||images.get(url)!==entry)return;entry.status='error';};
20
+ image.addEventListener('error',entry.failed);
21
+ image.style.display='none';element.appendChild(image);image.src=url;
22
+ };
23
+ v.unloadImage=url=>{const entry=images.get(String(url));if(entry){entry.image.removeEventListener('load',entry.loaded);entry.image.removeEventListener('error',entry.failed);entry.image.remove();images.delete(String(url));}};
24
+ v.isImageLoaded=url=>images.get(String(url))?.status==='loaded';
25
+ v.isImageLoading=url=>images.get(String(url))?.status==='loading';
26
+ v.isImageError=url=>images.get(String(url))?.status==='error';
27
+ const context = canvas.getContext('2d');
28
+ // QML Context2D.reset resets state and pixels. Reuse HTML canvas's
29
+ // dimension-reset behavior when the underlying 2D implementation lacks it.
30
+ const qmlContext = new Proxy(context, {
31
+ get(target, key) {
32
+ if (key === 'reset' && typeof target.reset !== 'function') return () => {canvas.width = canvas.width;};
33
+ if(key==='createImageData')return (...args)=>{
34
+ if(args.length!==1||typeof args[0]!=='string')return target.createImageData(...args);
35
+ const entry=images.get(args[0]);if(entry?.status!=='loaded')return null;
36
+ const tmp=document.createElement('canvas');tmp.width=entry.image.naturalWidth;tmp.height=entry.image.naturalHeight;
37
+ try{const ctx=tmp.getContext('2d');ctx.drawImage(entry.image,0,0);return ctx.getImageData(0,0,tmp.width,tmp.height);}
38
+ finally{tmp.width=tmp.height=1;}
39
+ };
40
+ const value = target[key];
41
+ return typeof value === 'function' ? value.bind(target) : value;
42
+ },
43
+ set(target, key, value) {target[key]=(key==='fillStyle'||key==='strokeStyle') && value && typeof value==='object' && 'r' in value ? api.cssColor(value) : value;return true;},
44
+ });
45
+ v.getContext = type => type === '2d' ? qmlContext : null;
46
+ Object.defineProperty(v, 'context', {get:()=>v.contextType === '2d' && v.available ? v.getContext('2d') : null});
47
+ v.requestPaint = () => {
48
+ if (disposed || frame !== undefined) return;
49
+ frame = requestAnimationFrame(() => {
50
+ frame = undefined;
51
+ if (disposed || !ready) return;
52
+ untrack(() => {
53
+ emit(v, 'onPaint', {x:0,y:0,width:canvas.width,height:canvas.height});
54
+ emit(v, 'onPainted');
55
+ });
56
+ });
57
+ };
58
+ v.markDirty = () => v.requestPaint();
59
+ v.requestAnimationFrame = callback => {
60
+ if (disposed) return 0;
61
+ const id = requestAnimationFrame(time => {callbacks.delete(id);if (!disposed) callback(time);});
62
+ callbacks.add(id);
63
+ return id;
64
+ };
65
+ v.cancelRequestAnimationFrame = id => {callbacks.delete(id);cancelAnimationFrame(id);};
66
+ effect(() => {
67
+ const w = Math.max(0,Math.ceil(v.canvasSize.width || v.width));
68
+ const h = Math.max(0,Math.ceil(v.canvasSize.height || v.height));
69
+ if (canvas.width !== w) canvas.width = w;
70
+ if (canvas.height !== h) canvas.height = h;
71
+ untrack(() => {ready=true;v.available=true;v.requestPaint();});
72
+ });
73
+ cleanup(() => {disposed=true;for(const url of images.keys())v.unloadImage(url);cancelAnimationFrame(frame);for (const id of callbacks) cancelAnimationFrame(id);callbacks.clear();});
74
+ }
75
+ return {make};
76
+ }