Skip to content

Releases: sachahjkl/wren

Wren 0.6.29

Choose a tag to compare

@github-actions github-actions released this 04 Nov 19:18
v0.6.29
b063c7a

Wren 0.6.29

Wren 0.6.29 can install its own updates, signed and checked, and brings devtools, JIT and compatibility work.

This is still a beta, under development.

Highlights

  • Signed updates (Windows): a release carries a manifest signed with a key of its own. About Wren checks for a newer release and, when you press "Install the update", downloads the program, compares its length and SHA-256 with the signed ones, and swaps it in with a way back. Nothing is installed without the button. The channel (address, files, key, signature required) is configurable: see docs/updates.md.
  • Developer tools: find in the panels, a console drawer, a colour picker, style values.
  • Faster scripts: the JIT compiles whole functions, calls, objects, strings and DOM getters.
  • Compatibility: SVG foreignObject, CSS module scripts, subresource integrity, script preparation, CSSOM rules and innerText, among many fixes (see the list below).
  • Help: the licence help button opens a GitHub issue form with your order number filled in.

Added

  • The update channel is configurable: browser.update.require_signature (on by default) can be turned off for a channel of your own that does not sign (About Wren then says the update is UNSIGNED and only the length and SHA-256 are checked), next to browser.update.url, browser.update.base_url and browser.update.public_key. docs/updates.md describes how to run a channel.
  • Signed updates (Windows). A release now carries update-windows-x64.json, a manifest (version, file, length, SHA-256) signed with a key of its own (not the licence key), and .json.sig. About Wren has an "Updates" section: "Check for updates" asks GitHub, then fetches the manifest and checks its signature, platform, announced version and that it is newer; "Install the update" (also the command "Install the update") downloads the program, compares its length and SHA-256 with the signed ones, writes it beside the program, reads it back, and only then swaps it in, putting the old one back on failure. Nothing is installed without the button. Development and labelled builds, and Linux, only check. wrentool update keygen|pubkey|manifest|sign|verify makes and checks the files; wrentool package -RequireUpdateManifest is used by the release workflow (secret WREN_UPDATE_SIGNING_KEY).
  • Values edited as in Chrome's Styles pane (src/browser/devtools_styles.odin): the number at the caret of a value steps with Up / Down (1), Shift (10), Alt (0.1), Ctrl (100), Page Up / Page Down (10) and the wheel over the focused field, in any value (margin: 10px 5px, rgba(...), calc()), and the page follows; a typed name is suggested the engine's properties, a typed value the keywords the engine takes for the property (Up / Down choose, Enter / Tab / a click take one); Tab and Shift+Tab go between name, value and the declarations around, Escape puts the declaration back as it was (one it added goes), and an !important declaration keeps it when edited.
  • A colour picker in the Styles pane (src/browser/devtools_style_edit.odin, the toolkit's ui.color_picker): a swatch before every colour of a value (hex, the colour functions, the names; several in a value), a click opens the picker on it (square, hue and opacity bars, the old and new colour, hex / rgb / hsl / oklch); the page follows each change, the colour stays in the format it was written in (hex stays hex, rgb(1, 2, 3) keeps its commas; a name, hwb() or lab() becomes hex), Escape takes the value back, Enter or a click elsewhere keeps it. The Computed view's swatches read every colour syntax too.
  • Escape shows the Console under the panel (Chrome's drawer, devtools_drawer_toggle): with the keyboard in the developer tools and nothing there taking it, Escape puts the Console in a stack of its own under the others (what "Show the Console under the other panels" does) and folds it away again; the layout keeps it.
  • Ctrl+F in the developer tools (src/browser/devtools_elements.odin, src/dom/xpath.odin): in Elements, "Find by string, selector, or XPath" finds the nodes whose tag, attributes or text hold the query, those the query selects as a CSS selector (the page's own matcher) and as an XPath 1.0 expression (paths and axes, predicates, unions, comparisons, contains, starts-with, text(), last(), count()...), together in document order; it says n of m, Enter / F3 go to the next match and Shift+Enter / Shift+F3 to the previous one, the tree opens and scrolls to it and the page shows its box, the matches follow the document as it changes, Escape closes it. Ctrl+F in the Console, the Network and the Application panels takes their filter field. F3 and Ctrl+F there are the developer tools', not the page's find.
  • Links that open in Wren: a web page can link to wren-https://..., wren-http://... or wren-about:wren, and the link opens in Wren (a new tab of the Wren that runs, else a new Wren) with its wren- prefix taken away. Setup and Repair register the three schemes (Windows: HKCU\Software\Classes; Linux: the desktop entry's MIME types), the uninstall removes them. Only http and https addresses and about:wren come out of a link: any other scheme, a control character or a link over 8 KiB opens nothing. wren.exe --open-url <link> is what the system runs (docs/desktop-integration.md).
  • sheet.media: a stylesheet's media is its MediaList (mediaText serialized, length, item(), media[i]); setting mediaText on a constructed sheet's list changes its media.
  • External profilers (docs/performance.md, Profilers): a build with WREN_TRACY=1 links Tracy's client (0.14.1, pinned; wrentool fetch tracy fetches its viewer), on demand, which shows the phases of each frame, the frames and each call of the JavaScript engine into the browser; the profile build is an ETW provider (Wren.Browser, TraceLogging) whose phases WPA shows as regions (tools/wren.wprp, tools/wren_regions.xml, wrentool trace start|stop); wasm.wasmtime.profiler=vtune names the compiled Wasm functions in VTune, the JavaScript engine's among them; threads are named for profilers and debuggers (wren-window, wren-style, wren-layout, the Wasm engine's). The default build links none of it.
  • CSS module scripts: import sheet from "./x.css" with {type: "css"} (and import() with it) gives a CSSStyleSheet of the file's rules, to adopt with adoptedStyleSheets, as web component libraries do. A URL imported with one type and then another fails as in other browsers.
  • HTML inside inline SVG (<foreignObject>): its content is laid out and painted in the SVG's coordinate space (viewBox, transforms), clipped by the <clipPath> around it and blurred by its feGaussianBlur filter. Google's new "G" logo (a conic gradient clipped to the letter) shows in colour.

Changed

  • Native functions called by compiled code cost less (src/engine/jsrt/jit.odin): the engine's helper calls them as the interpreter's hot loop does, without the generic call's dispatch. a.push(i) / a.pop() in a compiled loop 116 -> 107 ns, Set add/has 168 -> 150 ns, Math.sin / Math.pow 160 -> 147 ns.
  • The JIT reads DOM attributes (src/engine/jsrt/jit.odin): compiled code calls the native getters it finds on an object's prototypes (node.firstChild, list.childNodes.length, map.size), reads through the Web IDL prototypes of DOM nodes (dictionaries, guarded by a version of their keys) and treats DOM wrappers as ordinary objects; an exception in a getter unwinds exactly once, as if the interpreter had called it. A loop that walks a list of nodes reading four attributes 750 -> 530 ns an iteration, m.size in a loop 115 -> 48 ns; a loop that reads the global document is not compiled yet.
  • The JIT compiles loops that concatenate and compare strings (src/engine/jsrt/jit.odin): s += x and a + b with a string are made by the engine's own concatenation (its ropes and append buffers), a conversion that could run code or throw left to the interpreter so that it happens once, and === / switch compare strings by value. A switch on strings 105 -> 14 ns, s += "a" with charCodeAt 245 -> 127 ns.
  • The JIT compiles code that makes objects (src/engine/jsrt/jit.odin): new F(...) of a plain constructor, object literals and array literals in compiled code make their objects through the engine's allocator (a helper of the engine, the collector's rules unchanged), and a property written that the object does not have yet is appended through the shape's transition (a constructor's this.x = x). A loop that makes objects stays compiled; new Point(x, y) in a loop 404 -> 112 ns, {a, b, c} 261 -> 55 ns, [i, i + 1, i + 2] 123 -> 80 ns.
  • The JIT calls the script's getters and setters (src/engine/jsrt/jit.odin): a loop that reads a property through a getter of the script's (get x() {...}, a class's) or writes one through a setter, native or the script's, stays compiled, the accessor called as compiled code calls a function, an exception unwinding exactly once. A getter read in a loop 151 -> 78 ns, a setter 145 -> 81 ns, getter access of macro.js 184 -> 92 ns.
  • The classic style is the default: tabs are trapezoids attached to the toolbar unless browser.ui.chrome_style is set to modern (docs/chrome-style.md).
  • The pricing, terms and refunds pages (both styles) link to the licence help issue form instead of the bare issue list.
  • Compiled code allocates objects itself (src/engine/jsrt/jit.odin): an object literal and the object of new F() (the constructor's prototype guarded) are made by the code, from the engine's own pool and list of cells, without a call to the engine; the collector's contract is unchanged (allocation never collects). new Point(i, i) 110 -> 51 ns, {a, b, c} 57 -> 28 ns.
  • **The JIT compiles whole functions and calls compiled code directl...
Read more

Wren 0.6.28

Choose a tag to compare

@github-actions github-actions released this 04 Nov 01:05
v0.6.28
355edbc

Wren 0.6.28

Wren 0.6.28 gives its scroll bars room as Chrome's, docks its developer tools, keeps frames and popups in the history, and runs pages that rebuild their DOM faster again.

This is still a beta, under development.

Highlights

  • Classic scroll bars: page and element scroll bars take their room, with arrows at their ends (documentElement.clientWidth is the window less the bar, as in Chrome on Windows); scrollbar-gutter and scrollbar-width apply. The old overlay bars are layout.scrollbar.overlay.
  • Developer tools you dock: drag a tab onto a panel or an edge to split, onto a strip to join it, out to float it; panes resize from a few em to nearly the whole room and fold; the layout is kept in the profile.
  • History through frames: back and forward walk the navigations made inside frames, a frame keeps the Window of its first blank document, and so does a window opened by window.open; anchor links and back within a page scroll as the HTML standard says; scroll anchoring keeps the view when content loads above it.
  • Faster pages that rebuild their DOM: one call into the browser makes a node, the collection skips wrappers that hold nothing, the interpreter calls getters itself (a page that rebuilds 10,000 nodes every frame, measured on one machine: about 142 -> 163 frames a second).
  • More of the web platform: ::backdrop, ::placeholder, scrollend, document.domain, CSP Embedded Enforcement, clip, CDATA sections, namespaced attributes, URLPattern and URL setters as the standards say, SharedArrayBuffer between a page and its workers.
  • Rendering fixes from real sites: table rows grow to their cells, an absolutely positioned child of a flex or grid container is aligned, margins collapse through empty boxes, form controls take their own line height, style sheets resolve URLs against the base URL, and hovering one link of a menu lights only that link.
  • Names and keywords are matched ASCII case-insensitively everywhere, as the web platform says (a Kelvin sign is not a K).
  • Menus are dense and scroll when the window is too low; a right click on a download opens its actions; Setup's status line no longer blinks.
  • Built with Odin dev-2026-11, the C runtime linked statically on Windows.

Added

  • CSP Embedded Enforcement: <iframe csp="..."> (and iframe.csp) requires a Content-Security-Policy of the documents the frame shows. Its requests say which (Sec-Required-CSP); a page that agrees (Allow-CSP-From) or has a policy as strict is shown with it enforced, about:blank, srcdoc, data: and blob: documents take it, any other is refused as X-Frame-Options refuses. The sandbox directive of a Content-Security-Policy header now sandboxes the document.
  • document.domain setter: a page and its frames of other ports or subdomains of one site that set the same document.domain reach each other's documents and windows (HTML's same origin-domain), and a navigation between them fires the target's navigate event; a value that is not the host or a registrable suffix of it, or a sandboxed document, throws a SecurityError; under Origin-Agent-Cluster: ?1 the assignment changes nothing.
  • ::backdrop of the top layer: a modal dialog, a showing popover and the fullscreen element get a box the size of the viewport just under them, above the page (the UA's faint grey behind a modal dialog, black behind a fullscreen element, styled by the page); a click on a dialog's backdrop is the dialog's, a popover's lets it through. getComputedStyle(el, "::backdrop") and "::placeholder" answer, and ::part()::backdrop is a valid selector.
  • ::placeholder on text fields and textareas: its color, opacity, font (size, family, weight, style) and text-align apply; the default is Chromium's grey (#757575) instead of the field's color faded.
  • Scroll anchoring (CSS Scroll Anchoring, overflow-anchor): when content above what you are reading changes size (an image that loads late, a banner inserted or removed), the page keeps showing the same content instead of jumping; not at the top of a page, not where overflow-anchor: none says so. Reading scrollX / scrollY from a script gives the offsets after the layout the script's changes call for.
  • The scrollend event: once a scroll container or the page stops moving (a script's scroll, the wheel, the keys, smooth scrolling), at the first frame where it did not move again; an element's does not bubble, the document's reaches the window. A scroll event now also fires for a container a script moved and put back before the next frame.

Changed

  • WPT runner: windows with a history, a second http port: the windows a test opens keep a session history across their documents and follow history.back(), history.go() and navigation.traverseTo() between them, and the test server answers wptserve's second http port ({{ports[http][1]}}), so tests of another origin on another port run instead of timing out.
  • Styling no longer pays for ::backdrop on every element: the rules of ::backdrop (the browser's own, and resets such as Tailwind's *, ::before, ::after, ::backdrop) are tried only for the elements of the top layer (a modal dialog, a showing popover, the fullscreen element). A page of 10 000 elements restyled whole every frame: its style stage went from 0.84 to 0.75 ms. The static C runtime Wren links since Odin dev-2026-11 was measured against the dynamic one: the same speed. The numbers: docs/performance.md.
  • Pages that drop many nodes a frame collect them faster: the JavaScript engine no longer asks each node object a collection frees for the values it holds when it holds none, and the browser frees a removed tree without a list of its own for each. A page that builds and drops 10 000 nodes a frame: the collection that frees them went from 3.5 to 2.8 ms, its script stage from 12.2 to 11.4 ms a frame. The numbers, and what was measured and not kept: docs/performance.md.
  • Developer tools: panels you dock (the toolkit's docking, gui_toolkit 0.71). The panels (Console, Elements, Network, Application, Performance, Memory) are stacks of tabs: drag a tab out of its strip and squares show over the stack under the pointer (into it, or beside it at a side), at the edges of the panel and between the tabs of a strip, with a translucent preview of where it lands; let go elsewhere, it floats over the panel (moved by its strip, resized by its corner, docked back by a double click on its strip); Escape cancels. Each stack's menu (its button, a right click or the menu key on a tab) does all of it from the keyboard; Ctrl+Tab in a stack shows its next panel. The bars between stacks resize them and fold the second one (a double click, Enter). The Elements panel has the same for the DOM tree and the views of the chosen node (Styles, Computed, Layout, Event Listeners, Accessibility): Styles below the tree, Computed beside it, one of them floating. The layouts are kept in the profile (browser.devtools.layout, browser.devtools.elements.layout, one line each); they replace the drawer and the side pane's place (browser.devtools.drawer, drawer_tabs, drawer_tab, browser.devtools.elements.sidebar, side_tab), whose values a profile had become the new layouts (the drawer's tabs a stack under the others at its height, the side pane below the tree or folded as it was). By default the layout is the one before: one stack of the panels, the tree with its views at its right.
  • Built with Odin dev-2026-11 (odin.version and the CI's pins; the toolkit v0.70.0): wren.exe links the whole C runtime in (libcmt, libucrt, libvcruntime: Lexbor and the Rust libraries are built against it, as Odin's own C library is) and imports no runtime DLL; WOFF tables inflate through the new core:compress/deflate, bounded by their declared size.
  • Developer tools: panes you size. The panel docked under or beside the page goes from a header and a row to all the window but a strip of the page (it was held to 70 %); a floating one shrinks further. The Network tab's request details and the Application tab's value pane are resized by their bars (the network bar's drag ran away from the pointer; the value pane's bar did nothing).
  • Classic scroll bars (layout.scrollbar.overlay, off by default on every system, as Chrome and Edge on Windows and Linux): the page's scroll bars and those of scrollable elements take room instead of covering the content, with an arrow button at each end (a press scrolls a line, browser.scroll.key_line_px; held, it repeats after 250 ms every 50 ms):
    • the page's bar comes out of the initial containing block: document.documentElement.clientWidth is innerWidth less the bar (15 px, layout.scrollbar.classic.width_px), 100vw and media queries still see the whole window, a full-width footer ends where the bar begins; fixed boxes and sticky ones keep to what the bars leave;
    • an element with overflow: scroll has its bars whether it overflows or not, overflow: auto when it overflows (decided after a pass, laid out again: the first comparison of a layout may add or take bars away, the later ones only add, at most three more passes, so a bar that makes its content fit stays as in Blink); they sit between the border and the padding, a specified width keeps its border box (the bar comes out of the content: clientWidth less the bar), and an auto width grows by it;
    • scrollbar-width (thin: 11 px, layout.scrollbar.classic.thin_width_px; none: no bar, it still scrolls) and scrollbar-gutter (stable, stable both-edges, for overflow: auto and hidden, on the root element too) are honoured; scrollbar-color paints them;
    • colors: layout.scrollbar.classic.thumb_color, track_color, arrow_color, and browser.scrollbar.dark.classic_* in the dark scheme;
    • a viewport less than two arrows long either way (a tiny frame) h...
Read more

Wren 0.6.27

Choose a tag to compare

@github-actions github-actions released this 03 Nov 10:34
v0.6.27
c4b7bd8

Wren 0.6.27

Wren 0.6.27 fixes links that all showed their hover style together, and follows the standards more closely for names, international domain names and forms.

This is still a beta, under development.

Highlights

  • Hovering one link no longer restyles its look-alikes: links that share a style (a site's nav, the start page's
    Configuration, History and Content blocker links) each show their hover state alone again.
  • International domain names follow the URL Standard: xn-- hosts are only lowercased, labels starting with a
    combining mark or with a misplaced zero-width joiner are refused.
  • Names as scripts give them: createElementNS and setAttributeNS keep their case, namespace selectors
    (*|div, |div) work, and an iframe answered as XML is shown as an XML document.
  • Two forms sent at once to two frames both arrive (the second used to cancel the first).
  • A setTimeout(0) set by a script runs after the tasks that script queued, as in other browsers.
  • XHR and FileReader text honour a byte order mark.
  • Full style passes are faster again (the page's color scheme read from its <meta> elements, subtrees weighed
    in one walk), and pages with columns build their display list on the layout threads.

Changed

  • Pages that restyle everything style faster: before the style threads share a full pass, the browser weighs the document's subtrees in one walk instead of three, and a pass takes the slots of the pass recycled before it instead of faulting fresh memory in. A page of 10 000 elements restyled whole every frame: its styles 0.74 ms a frame instead of 0.90, its frames' CPU work 3.25 ms instead of 3.77. The numbers: docs/performance.md.
  • Pages with columns and pages that restyle everything lay out and style faster: the display list of a page with columns is made on the layout threads as on any other page, a layout with columns and no vertical writing mode no longer turns every box to physical coordinates for nothing, a full style pass no longer walks the whole document for a <meta name="color-scheme"> nor asks the forms model whether every element is disabled, and the comparison of a full style pass with the one before compares each pair of shared styles once. A page that rebuilds a list of 2 500 items in six columns every frame: its layout 3.2 ms a frame instead of 3.6, its styles 1.7 ms instead of 1.85; a page restyled whole every frame: its styles about a third faster. The numbers: docs/performance.md.

Fixed

  • wrentool compare and the benchmarks run in a fresh checkout: the folder of their programs' logs (build\bench) is made when missing, where Edge failed to start ("its log cannot be opened") and no comparison was made.
  • Hovering one link of a nav no longer paints every link of it in its hover style (the new tab page's links, a site's menu): alike elements share one style record, and the paint-only refresh of the display list re-pointed items by their old record, so every link that shared it took the hovered one's new style. Each display item now takes its own box's or inline item's style.
  • classList.add() and remove() without a token, replace() with an empty one: add() and remove() with no argument still write the attribute back in its serialized form ("a b c a " becomes "a b c"), as the DOM's update steps say, and replace() with an empty token throws a SyntaxError before looking for whitespace in the other.
  • Text, comments, fragments and processing instructions of a document of its own are its: what createTextNode, createComment, createDocumentFragment and createProcessingInstruction of a document of its own (createHTMLDocument's, new Document(), DOMParser's) make has that document as ownerDocument, as its elements already had.
  • XMLHttpRequest and FileReader decode text as the Encoding Standard says: a byte order mark chooses the encoding (a UTF-16 response without a charset, or with one it contradicts, is read as UTF-16), and a label of the replacement encoding (iso-2022-kr, hz-gb-2312...) gives one U+FFFD instead of the text read as UTF-8.
  • Forms submitted to several frames at once: a page that submits two forms in a row, each to a frame of its own, navigates both frames (the second submission replaced the first, whatever its target: a frame was left waiting forever). A newer submission still replaces one planned for the same target. WPT encoding: the first part of each of the 45 form-encoding tests no longer times out, 18000 more subtests pass.
  • A timer runs after the tasks queued before it: a setTimeout(0) set from a task of the page (a script the parser runs while the page loads) ran before the tasks that script had queued, so a dialog's toggle event came after it; it runs after them now, as it did from a timer's script. WPT toggle-events.html passes whole.
  • International domain names as the URL Standard has them: a host all of ASCII is only lower-cased (an xn-- label that does not decode, or decodes to what UTS #46 would refuse, no longer makes the URL invalid: xn--a, xn--unicode-.org); no length of a domain or of its labels is verified; a label beginning with a combining mark, or with a zero width joiner or non-joiner out of the context CheckJoiners allows, is refused (Lexbor 3.0.1 does not check them: src/shim/wb_idna.c, its tables generated from the Unicode Character Database by wrentool gen idna). WPT url 10946 -> 12056 passing.
  • XML documents in frames: a frame's response of an XML type (application/xml, text/xml, +xml) is an XML document (it was shown as text), with its contentType; document.open(), write() and close() of an XML document throw an InvalidStateError as HTML says. WPT dom/nodes +170 (the XML frames of createElement and createElementNS), custom-elements +22.
  • Names as scripts give them: createElementNS and setAttributeNS keep the case of the names they are given (localName, prefix, attributes), an element's tagName upper-cases its whole qualified name in ASCII only, setAttribute on an element of another namespace than HTML or SVG keeps its case, and getElementsByTagName compares names as the DOM says; the local name of a qualified name is all after its first colon (f::oo), any doctype name without whitespace, NULL or > is valid, and createDocument's content type follows its namespace argument. Selectors take the namespace prefixes *| (any namespace) and | (none: |div), in type selectors and attribute selectors. WPT dom/nodes 10518 -> 11073 passing.
Changes since v0.6.26
  • flake.nix: the Linux release 0.6.26
  • Remote channel: what the pipe server and the socket server share in server.odin
  • Tools: the process's memory from base.process_memory
  • One disk cache of engine output (wasm.Disk_Cache, disk_cache.odin) for the compiled-code cache and the JavaScript bytecode cache: the profile key, the signed files, the writer thread, the trim
  • One escaper of text for HTML and XML (base.write_escaped, base.escaped: & < > always, what Escapes adds: the double quote, the single quote, U+00A0 as  ) for the browser's pages (demos …
  • Numbers as text: base.itoa for the browser's forms and search engines, CSP's ports, WebSocket's host keys, the Linux desktop's icons and remote/api's /proc paths
  • Copies of strings and bytes by strings.clone and slice.clone
  • One escape of a profile file's fields (base.field_escape, field_escaped, field_unescape: backslash, tab, line breaks and NUL) for history.txt, session.txt, the search engines and the service workers' …
  • JS glue: the interfaces built on a prelude
  • JS glue: one ascii_ieq (wj's; web_ascii_ieq calls it), one is_nan and is_finite (wj/libc.odin), one is_ws (reflect.odin) in place of their copies
  • A hex digit's value from base.hex_value (a byte or a rune, -1 when not a digit) for the blocker's scriptlets and regular expressions, CSS's colours and escapes, the forms' patterns, the browser's …
  • One writer of JSON strings (base.write_json_string, base.json_quote: invalid UTF-8 as U+FFFD, U+2028 and U+2029 escaped), moved out of remote's protocol for the remote channel and the CDP, the …
  • Writes through a temporary file renamed over the target by base.file_write_atomic
  • An origin's file name in the profile by base.write_file_stem
  • Web tests: http_get (through fetch's loader), net_get (the network client alone) and enc in web_harness.odin, their copies in the images, media, netapi, wsclose, frames, jsload and sitecompat tests …
  • e2e: enter gives the window back at the end of the caller's scope by itself
  • Blocker: the HTML filters use procedural.odin's close_paren and glob_match and rewrite.odin's contains_word
  • Tools: tooling.median (blockerpages, wasmbench, endurance), tooling.parse_digits (the lenient parse_int of adblockbench, jsengine, jspage, test262 and the netapi test's count_of) and …
  • Adblock: what $removeparam, $csp and $replace share in request_matches
  • Media: the events of MediaSource, the track lists and the cues are posted by one post_object_event
  • Names as scripts give them: createElementNS and setAttributeNS keep their names' case
  • WPT expectations: encoding's utf-32 tests read the XML frames as XML documents now
  • URL: the URL Standard's domain to ASCII where Lexbor's UTS 46 differs
  • Event loop: a timer set by a posted task waits for the next turn, behind what that task posted
  • Forms: a planned navigation per target (Page_Forms.pending: Planned_Navigation, planned_for), a newer submission replacing only the one of its target: two forms submitted at once to two frames both …
  • XMLHttpRequest and FileReader text: Encoding's decode
  • Tests: FileReader's readAsText with a byte order mark and with a label of the replacement encoding
  • Documents of their own: createTextNode, createComment, createDocumentFragment and createProcessingInstruction o...
Read more

Wren 0.6.26

Choose a tag to compare

@github-actions github-actions released this 03 Nov 07:08
v0.6.26
bac4694

Wren 0.6.26

Wren 0.6.26 runs pages that build large documents again much faster, and reads pages written in legacy encodings right.

This is still a beta, under development.

Highlights

  • Pages that rebuild their DOM are much faster: elements alike share their selector matching, document and window
    stay on the interpreter's fast path, a wrapper is one allocation, string arguments are read in place and a collection
    unpins its nodes in one call (a page that rebuilds 10 000 nodes every frame, measured on one machine: about 85 -> 142
    frames a second since 0.6.25).
  • Legacy encodings: a page in Shift_JIS, EUC-KR, Big5, GBK or windows-1252 keeps its encoding for its links and forms
    (a search form no longer sends mojibake), document.characterSet says it, level-2 kanji decode, and documents made by
    a script keep their own encoding and URL.
  • getComputedStyle gives the used insets (top, right, bottom, left in pixels) of positioned elements:
    what tooltip and popover libraries read.
  • <dialog>: beforetoggle and toggle events, closedBy, and a modal dialog leaves the top layer when it is taken out
    of the document.
  • document.createProcessingInstruction, and an error of a method of another frame's node is of that frame.
  • Text on several threads waits less (the graphics toolkit's atlas and word cache locks).

Added

  • document.createProcessingInstruction makes a ProcessingInstruction (it was not a function in HTML documents; the XML parser already made them), InvalidCharacterError for a target that is not an XML name or data holding ?>.

Changed

  • Scripts that build and drop many DOM nodes run faster again: the collection that frees dropped nodes unpins them in one call to the browser instead of one per node, finds their script objects in an array by node instead of a hash table, the string arguments of DOM calls are read where the engine keeps them instead of being copied (ASCII text), and the names of the known elements are no longer copied for each element. A page that rebuilds a list of 2 500 items every frame: its script 14.3 ms a frame instead of 18.9, 142 frames/s instead of 119; the collection of the dropped nodes 3.8 ms instead of 5.4. The numbers: docs/performance.md.
  • Scripts that build and drop many DOM nodes run faster: document and window no longer ask for named properties nobody implements on every property read, a wrapper's interface is found by the element name's first byte, an insertion walks the inserted nodes once instead of three times, a node's script object takes one allocation instead of three, the engine's API reads a value without decoding it, and a DOM getter is called where it is found. A page that rebuilds a list of 2 500 items every frame: its script 17.6 ms a frame instead of 26.7, 122 frames/s instead of 93; the collection of the dropped nodes 5.3 ms instead of 8.1. The numbers: docs/performance.md.
  • Alike elements share their selector matching: siblings with the same tag and attributes, and their cousins, take what the first of them matched and match again only the selectors that tell them apart (their position, their state, their content, an ancestor's), so a full style pass of a page made of repeated items costs a quarter to nearly two fifths less (a page that rebuilds a list of 2 500 items every frame: 1.8 ms a pass instead of 3.0, and 92 frames/s instead of 86). A partial layout that would end up rebuilding too much finds it out before looking for what to rebuild (that page's layout wait: 3.3 ms instead of 4.7). The numbers: docs/performance.md.
  • Text on several threads waits less: a word's shape is read under a shared lock, and the glyphs of a run take the atlas lock once (font.glyph_images, font.slug_glyphs); the toolkit's helpers (the emoji font, wgpu-native) are fetched by its Odin tool tools/tk.

Fixed

  • <dialog> as the standard has it now: opening and closing a dialog fire beforetoggle (cancelable when it opens: a listener can keep it closed) and a toggle event from a task, coalesced as a popover's; dialog.closedBy reflects the closedby attribute (closerequest for a modal dialog without one, else none) and Escape leaves a modal dialog with closedby=none open; show() of a dialog open as a modal one throws InvalidStateError; requestClose() does nothing for a dialog out of the document or from its own cancel listener; a modal dialog taken out of the document leaves the top layer (put back, it is laid out where it is, not centered).
  • Legacy encodings, the rest of the way: in a Shift_JIS page the IBM extensions (Roman numerals, ⅰ, 纊...) go into links and forms as the IBM bytes (0xFA..0xFC) the standard and every server expect, not as NEC's duplicates; in GBK and gb18030 the private use code points GB18030-2022 gave to real characters encode as their bytes; TextDecoder and charset know the labels the standard added (unicode, ucs-2, unicodefffe, unicode20utf8...) and refuse a label padded with a vertical tab or a no-break space. A frame's document that declares no encoding takes its same-origin parent's (it was windows-1252), and a text or JSON document in a frame is decoded in its response's charset. WPT encoding: 99.2 % to 99.99 % of its subtests.
  • A method of a frame's node given too few arguments throws the frame's TypeError (e instanceof frame.contentWindow.TypeError), as in other browsers; it was the caller's since each window has its own built-ins.
  • Documents of their own keep their nodes: an element made by a document of its own (createHTMLDocument's, new Document(), createDocument's, DOMParser's), adopted (adoptNode) or imported (importNode) into one, or removed from one, has that document as ownerDocument (it was the page's until inserted), and its links encode their query in UTF-8 whatever the page's encoding; createDocumentType of such a document's implementation makes a doctype of it. document.URL and documentURI of createHTMLDocument's, createDocument's, new Document() and parseHTMLUnsafe's documents are about:blank, not the page's. With createProcessingInstruction and the frames' TypeError, 284 more subtests of WPT dom/nodes pass.
  • getComputedStyle gives the used insets of positioned elements: top, right, bottom and left of an absolutely positioned, fixed, relative or sticky element are pixels (a percentage against its containing block, an auto side where the box is, or the opposite side negated for a relative one) instead of the declared 10% or auto that position libraries (tooltips, popovers, sticky polyfills) could not use; a sticky element keeps auto. Numbers of computed values are the shortest that read back the same (20.7px, not 20.700001px). WPT css/cssom: 56 % to 78 %.
  • Pages in legacy encodings (Shift_JIS, EUC-KR, Big5, GBK, windows-1252...) link and submit in their encoding: document.characterSet says the encoding a page was decoded from (it always said UTF-8; a document of its own, createHTMLDocument's or DOMParser's, is UTF-8, and so is an XHTML page that declares none, as XML wants), the query of its links (a.href, links a script or the user follows, against the <base> too) is percent-encoded in it, and forms submit in it (or in the first encoding accept-charset names): query, urlencoded, text/plain and multipart bodies, _charset_, &#N; for what the encoding cannot represent. A search form of a Shift_JIS site sent mojibake. Shift_JIS text with a lead byte between 0xE1 and 0xFB (the level 2 kanji, the IBM extensions) is no longer U+FFFD, in pages and in TextDecoder. WPT encoding: 2.4 % to 97.7 % of its subtests.
Changes since v0.6.25
  • flake.nix: the Linux release 0.6.25
  • Toolkit 0.64.0: its helpers fetched through tools/tk
  • Style: matching shared between siblings and cousins
  • Layout of DOM-heavy pages: an incremental attempt gives up before its search of what to rebuild when the boxes of the removed elements alone pass the limit
  • Performance, round 5 measured (docs/performance.md: matching shared by alike elements, the counter walk only when a box shows a counter, the check of what was inserted on bits, the incremental …
  • docs/performance.md: item 6 after toolkit 0.64.0
  • Legacy encodings: a page keeps the encoding it was decoded from
  • getComputedStyle: the used insets of positioned elements
  • Script stage of DOM-heavy pages: Document and Window plain platform objects
  • Performance, round 6 measured (docs/performance.md: the named class, the interface by name, the insertion walks, the wrapper's allocations, the C API's decoding, the getters
  • test_all: judge capi is a step (the engine's C API natively, 48 checks: it ran only by hand)
  • Toolkit 0.65.0 (its unused public procedures removed: none was Wren's)
  • Script stage of DOM-heavy pages, round 7: a collection unpins the nodes of the wrappers it finalized in one call
  • Performance, round 7 measured (docs/performance.md: the collection's unpins in one call, the arguments' strings read in place, element names as the program's strings, the wrappers by handle index
  • Documents of their own: a node's node document
  • Encodings, the rest of the way: Shift_JIS encodes the IBM extensions as the standard does
  • : show() and showModal() past their checks fire a cancelable beforetoggle and queue toggle
  • : a modal dialog taken out of the document leaves the top layer and is not modal when it comes back
  • Docs site: node documents, encodings, the dialog's toggle events and closedBy, the WPT runner's testdriver, the conformance numbers
  • Wren 0.6.26

Wren 0.6.25

Choose a tag to compare

@github-actions github-actions released this 03 Nov 00:29
v0.6.25
bf723f7

Wren 0.6.25

Wren 0.6.25 opens the DOM of same-origin frames, lays out columns and vertical text, and sleeps when nothing changes.

This is still a beta, under development.

Highlights

  • Same-origin frames: contentDocument, contentWindow.document, parent.document; each frame keeps its own
    built-ins (instanceof across frames is false, as in other browsers); a frame's navigations fire their navigate
    event and keep their own session history.
  • Multi-column layout and vertical writing modes: columns, column-span, nested columns, writing-mode,
    text-orientation, and documents that scroll horizontally or start from the bottom.
  • A GPU process for WebGL, WebGL in workers and transferControlToOffscreen, WebGL 2 transform feedback and queries.
  • AudioWorklet, and P-521 in crypto.subtle.
  • Pages that build large documents run about three times faster: identical styles shared, columns laid out on the
    layout threads, wrappers of plain nodes freed without the cost of a full collection (a page that rebuilds 10 000 nodes every frame,
    measured on one machine: about 28 -> 85 frames a second).
  • An image opened by itself is a viewer: zoom around the pointer (Ctrl+wheel, pinch, + and -), drag to pan, R to
    turn it, a click between fitted and 100 %.
  • A browser at rest draws nothing: no frame and almost no wake on a page that changes nothing, and a caret that blinks
    redraws only its own pixels.
  • Faster and safer closing: no wait on the shutdown watchdog, the graphics device let go before the end, wren ctl stop immediate on a throwaway profile.
  • The interface has its own zoom (Ctrl+wheel over the tabs or toolbar), and its scroll bars show only when useful.
  • Crashes and hangs fixed: WebGL while the GPU process restarts, an iframe moved into its own document, AV1 on a
    machine short of processors, large Web Audio graphs, a worker freeing a page's localStorage, WebSockets that stopped
    after a large message.
  • The User-Agent carries the Chromium tokens before Wren/<version>, so sites that check for them (Google Sign-In)
    answer, and scripts get 30 seconds before they're stopped.

Added

  • Windows that scripts open: window.open() to _blank or to a new name, and a script's click on a link to one, open a tab whose window has an opener (window.opener), a name other windows find, postMessage both ways, closed, and close() by the script that opened it; noopener and noreferrer (features, or rel) cut the relationship and window.open then returns null. A Cross-Origin-Opener-Policy that does not match the opener's severs it as other browsers do: the popup's opener is null and the opener sees the popup closed. The WPT runner's pages open the same windows, without a shell.
  • Names resolved by the browser itself, and connections kept on this machine (built-in network stack): network.hosts (name=address pairs; a name stands for its subdomains too) answers those names without asking the system or a hosts file, and network.loopback_only (or WREN_NETWORK_LOOPBACK_ONLY=1) makes any connection to an address that is not this machine's fail at once, named on the standard error. The tests run with both: web-platform-tests under its own names (web-platform.test, www1.web-platform.test...), and nothing tested reaches the Internet.
  • wren ctl stacks <process id> prints the call stack of each thread of a running process, named with its PDB (--dump adds a minidump). wren ctl quit prints them when the Wren does not end, and the shutdown watchdog leaves them beside shutdown.log before it ends a stuck browser.
  • WebGL in workers: OffscreenCanvas.getContext("webgl") and ("webgl2") in a worker, transferToImageBitmap, and canvas.transferControlToOffscreen(), whose OffscreenCanvas goes to a worker in a message and whose frames (WebGL or 2D) reach the page's canvas.
  • A flake.nix: nix run starts the latest Linux release, nix develop gives what build.sh needs.
  • Reference tests in the WPT runner (tools\wpt.ps1 -Reftests, css/css-multicol and css/css-writing-modes for now): the test and its references laid out headless, drawn by a software rasteriser over the display list (src/tools/wpt/reftest: clips, transforms, opacity groups, backgrounds, gradients, images, shadows, borders of every style, Ahem exactly, other fonts as deterministic patterns), compared under WPT's rules (<meta name=fuzzy>, chains of references, reftest-wait). They count apart from testharness tests, with expectations of their own (wpt/expectations/<folder>.reftest), and the three pictures of a failure can be dumped outside the repository (-ReftestDump).
  • Vertical writing modes: writing-mode (vertical-rl, vertical-lr, sideways-rl, sideways-lr), text-orientation, text-combine-upright; flow-relative properties mapped per element for its writing mode and direction (margin-inline-start in right-to-left text too); orthogonal flows; flex, grid, tables and columns in vertical text; a vertical document, whose scroll goes left; anchor positioning follows writing modes. Demo: examples/site/vertical.html.
  • Multi-column layout: columns, column-count, column-width, column-gap, column-rule, column-span: all, column-fill, break-before, break-after, break-inside, orphans, widows, with balancing, overflow columns and nested containers; boxes are cut across columns, getClientRects answers per fragment (and per line for inline boxes), hit testing, selection and painting work column by column. Demo: examples/site/multicol.html.
  • The document scrolls horizontally: a page wider than the viewport has a horizontal scroll bar at the bottom and moves with a horizontal wheel, Shift+wheel, the left and right arrows, a pan, autoscroll and a selection dragged past an edge, smoothly as vertically. window.scrollX, scrollTo, scrollBy, scrollLeft, scrollWidth and scrollIntoView work on that axis, rectangles are relative to the viewport on it, position: sticky sticks to its edges, and the position comes back with the history. overflow-x: hidden or clip on the root forbids it; with direction: rtl on the root the offsets go below 0, to reach what overflows to the left.
  • P-521 in crypto.subtle: ECDSA and ECDH on the curve, with generate, sign, verify, deriveBits, deriveKey, import and export in raw, spki, pkcs8 and jwk, wrap and unwrap, off the page's thread as the other curves are. The arithmetic is RustCrypto's p521 crate (native/crypto).
  • WebGL 2 transform feedback and real queries: beginTransformFeedback, transformFeedbackVaryings, getTransformFeedbackVarying, bindBufferBase/bindBufferRange on TRANSFORM_FEEDBACK_BUFFER, pause/resume (a variant of the vertex stage that writes to storage; an adapter without that says MAX_TRANSFORM_FEEDBACK_* are 0), ANY_SAMPLES_PASSED and ANY_SAMPLES_PASSED_CONSERVATIVE by occlusion queries, TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, with results on a later task only; a vertex attribute divisor above 1.
  • AudioWorklet: audioContext.audioWorklet.addModule(), AudioWorkletGlobalScope with registerProcessor, AudioWorkletProcessor and AudioWorkletNode with parameters, port and processorerror, in realtime and offline contexts. The processors run in a realm on a thread of their own; a quantum waits for it with a deadline and is silence on a miss, and a realm that throws, stops or hangs (webaudio.worklet_stall_ms) is processorerror, never a hung render thread.
  • The browser's interface has its own zoom, independent of pages: Ctrl+wheel with the pointer over the tabs or the toolbar, Ctrl+Plus, Minus and 0 while the interface has the keyboard, or from the command palette. It's kept in browser.ui.zoom.
  • The DOM of a same-origin frame is reachable: iframe.contentDocument, contentWindow.document, parent.document, top.document and frameElement are real documents and elements, with listeners from both realms, inserted scripts running in the frame's realm, document.open / write / close, and blank frames that show what is written in them. frame.contentWindow.foo and parent.foo() work through a value bridge between realms (primitives copied, objects and functions as proxies). A cross-origin or sandboxed frame stays out of reach by every route, with the origin checked on each call (docs/frames.md).
  • An image opened by itself is a viewer: Ctrl+wheel (and a touchpad's pinch) zooms the image around the pointer, + and - zoom around the center and 0 fits it again; a click toggles fit and 100 %; the image is dragged with the pointer (grab, grabbing), moved with the wheel and the arrows when it is larger than the window; R and Shift+R (and the context menu) turn it a quarter; a double click resets the view. Ctrl+Plus / Minus / 0 still zoom the page.

Changed

  • One test program. The web tests moved into src/tools/e2e (web_<subject>.odin): build\web_test.exe and build\e2e_test_check.exe are gone, build\e2e_test.exe remains, with every allocation tracked: every scenario now ends with nothing left allocated, or fails. The GPU process's life is tested once, in the real browser, with nothing asked of the program: killed and suspended from outside, under a page, two contexts and a worker; the browser has none of the hooks that test had (they stay for the tests of native/webgl only).
  • The test tooling is in Odin: src/tools/test_all (a typed table of steps, a small state machine, the result cache, the profiler, its own desktop; on Windows and Linux) instead of tools\test_all.ps1, test_cache.ps1, test_profile.ps1, tools/linux_ci.sh; src/tools/wpt and src/tools/test262 are the whole of their runs (the pinned checkout, the second chance, -update); src/tools/buildjs builds the JavaScript engine, src/tools/endurance is t...
Read more

Wren 0.6.24

Choose a tag to compare

@github-actions github-actions released this 01 Nov 04:19
v0.6.24
c30e4f8

Wren 0.6.24

Wren 0.6.24 draws WebGL and positions with CSS anchors: Three.js and MapLibre run.

This is still a beta, under development.

Highlights

  • WebGL and WebGL 2, through wgpu and naga, with a device and a thread for each context. Three.js examples and MapLibre maps run: a simple scene at 60 frames a second, a heavy one at 20 to 30, on a good GPU. A machine with no adapter gets null, as a page expects. Transform feedback and real queries are not there yet, nor WebGL in workers. There is no separate GPU process: a crash inside a driver takes the browser down.
  • CSS anchor positioning: anchor(), anchor-size(), position-area, position-try-fallbacks, @position-try, position-visibility, implicit anchors of popovers. The web-platform-tests of the folder go from 13.7 % to 85 %. No writing modes or anchor names across shadow trees.
  • A compiled loop's return no longer leaks stack depth: pages could end in a false "Maximum call stack".
  • TypedArray.prototype.set no longer traps on large arrays.
  • Scroll containers count the end margins of their children as scrollable overflow, as other browsers do: scrollHeight may grow.

Added

  • WebGL 1 and 2: canvas.getContext("webgl") and ("webgl2"), on a thread and a wgpu device per context (Vulkan or Direct3D 12 on Windows, Vulkan on Linux, the system's software rasterizer without a GPU), GLSL ES through a rewrite in front of naga. Three.js examples and MapLibre GL JS work. No transform feedback; see docs/js/webgl.md for the limits, and the webgl.* preferences.
  • CSS anchor positioning: anchor-name, anchor-scope, position-anchor, anchor() and anchor-size(), position-area, anchor-center, position-try-fallbacks, position-try-order, @position-try, position-visibility, the implicit anchor of a popover. Tooltips follow an anchor that scrolls, flip when they no longer fit, and pages no longer load the @oddbird/css-anchor-positioning polyfill (it ran past the script limit on chatgpt.com).
Changes since v0.6.23
  • CSS anchor positioning: anchor(), anchor-size(), position-area, fallbacks, position-visibility
  • anchor positioning: anchor-center in all of the rectangle, what a frame chose is what stays, used insets
  • wpt: css/css-anchor-position, run with a layout
  • A demo of CSS anchor positioning in the demos page
  • anchor positioning: a clamped position-area box against the scrolled containing block
  • CSS anchor positioning in the documentation, a changelog fragment
  • The demos index lists the anchor positioning demo
  • wpt: css/css-anchor-position recorded with time for a cold start
  • Site docs regenerated with the anchor page
  • WPT expectations after anchor positioning and Web Audio
  • Site docs after the expectations
  • WebGL's device side: a crate with a GLSL ES translator over naga and GL's state machine over wgpu
  • canvas.getContext("webgl") and ("webgl2"): the interfaces in lazily evaluated JavaScript, a context on its canvas's backing
  • JIT: a return from inside a compiled loop in a function called from JavaScript
  • WebGL: what Three.js needs of GLSL ES and of the page
  • WebGL: limits a hostile shader cannot pass, frames without a conversion, IDL lengths, WPT webgl, documentation
  • TypedArray.prototype.set of a large array traps no more
  • WebGL: the SDK's module compiles (a parameter named like an Odin keyword), the demos index is up to date
  • jswebtest webgl: no slow-script limit for a page that waits for the software rasterizer on a loaded machine
  • jswebtest webgl: the slow-script limit is two minutes, not none
  • Site docs with WebGL
  • linux_smoke checks WebGL; a paint timing test of WebGL may time out in the runner
  • Release 0.6.24: WebGL and WebGL 2, CSS anchor positioning

Wren 0.6.23

Choose a tag to compare

@github-actions github-actions released this 01 Nov 00:21
v0.6.23
0e84887

Wren 0.6.23

Wren 0.6.23 plays Web Audio, and vscode.dev, tldraw and reddit work.

This is still a beta, under development.

Highlights

  • Web Audio. AudioContext, OfflineAudioContext, the usual nodes, decodeAudioData and ScriptProcessorNode, on Windows and Linux, following the autoplay policy. The web-platform-tests of webaudio go from 24 % to 92 %. AudioWorklet is not there yet.
  • vscode.dev works: its compile no longer runs into a time limit, large scripts compile about 3.6 times faster, a service worker's opaque answer reaches the page, and a <meta> policy no longer applies to code before it.
  • reddit works: a form submitted while the page loads goes through, a hang that grew to 22 GB of memory is gone, and getComputedStyle answers for shorthands.
  • tldraw's icons show: an SVG image's fragment now makes :target match.
  • Regular expressions search faster: Octane's regexp from 127 to about 165.
  • A same-origin frame gives navigator, screen and crypto, which scripts often take from a frame they just made.

Added

  • Web Audio: AudioContext and OfflineAudioContext with their nodes (buffer sources, oscillators with PeriodicWave, gain, constant source, biquad and IIR filters, delay, stereo and equal-power panners, splitter, merger, compressor, wave shaper, analyser, convolver, ScriptProcessorNode, MediaElementAudioSourceNode), AudioParam with all its automation, and decodeAudioData (off the page's thread: MP4, WebM, WAV, MP3, AAC, FLAC, Ogg). The graph renders in the browser (src/webaudio) on a thread per running context; media.autoplay.policy holds a context until a click or a key press; the tab's mute and speaker follow it. No AudioWorklet, MediaStream nodes or HRTF yet (docs/js/web-audio.md). The web-platform-tests of webaudio/ go from 24.4 % to about 90 %.

Changed

  • Regular expressions search faster: a regexp that is not sticky no longer goes through a bytecode loop for each position of the subject, and skips at once the positions where its first character or range cannot match; replace copies the text between matches in one go. Octane's regexp from 127 to about 165 (measured with other work on the machine).

Fixed

  • Long Animation Frames entries no longer carry garbage as the invoker's name, which made the page's PerformanceObserver throw JSON.parse: bad escape (reddit.com): the name was kept by reference, and read after it was gone when an event had no listener.
  • getComputedStyle answers for shorthands (border-radius, border, border-*, outline, inset, gap, flex, flex-flow, text-decoration, background, font) and mask-image instead of an empty string, and margin and padding in their shortest form: reddit.com's scripts threw on the NaN they made of it.
  • An element removed from a page with a shadow root and rules on form controls (:valid, :checked...) no longer hangs the browser while it takes all the memory: a walk of the document went from a shadow root to its host and back for ever (reddit.com, 22 GB in five minutes).
  • A form submitted while the page loads (a login's automatic POST, reddit's check page) navigates: it reached the browser before the shell had attached itself to the page, and was dropped with form submission ignored: no navigator.
  • The console explains an InternalError: interrupted from a promise reaction or an async function (javascript.slow_script_ms), as it did for a script, and no longer reports an error for a worker terminated while it starts.
  • A same-origin frame's navigator, screen and crypto no longer throw a SecurityError: the frame gives this realm's own, as it did for console (x.com's scripts read them from a blank frame).
  • An SVG image addressed with a fragment (icons.svg#save, in <img>, background-image or mask-image) gets :target in its style sheets: sprites that show one icon that way no longer come out empty (the icons of tldraw.com).
  • A <meta> Content-Security-Policy applies to inline code and eval from where it stands: a script the parser ran before it is no longer refused.
  • A service worker answering a no-cors request with an opaque response (respondWith(fetch(e.request)), a stylesheet or script of another origin, also from the Cache API) no longer gives the page an empty body: the browser fetches that address for the page, scripts in the worker still never see the bytes. Such a response from the Cache API stays opaque, and a request that is not no-cors gets a network error. A second visit to vscode.dev stayed blank.
  • Compiling a large bundle no longer compares every reference with every top-level name: three to four times quicker on the 21 MB of vscode.dev, the same bytecode.
  • Large scripts compile on their background threads whatever wasm.max_call_ms says: past that limit (1 s) the thread's engine died and the interface thread compiled that bundle and all later ones itself, inside the time javascript.slow_script_ms allows a task (vscode.dev stayed blank).
Changes since v0.6.22
  • Regexp search outside the bytecode, skipping positions that cannot match
  • jsrt: name the bare numbers that carry a decision
  • jscapi: the default stack size is jsrt's
  • frontend: name the bare numbers that carry a decision
  • idb: name the bare numbers that carry a decision
  • forms: name the bare numbers that carry a decision
  • a11y: name the bare numbers that carry a decision
  • dom: name the bare numbers that carry a decision
  • httpcache: name the bare numbers that carry a decision
  • js: name the bare numbers that carry a decision
  • css: name the bare numbers that carry a decision
  • url: name the width of a character range the pattern engine folds
  • webfont: name the bare numbers that carry a decision
  • native/js/odin: name the bare numbers that carry a decision
  • browser: name the control and row heights that about:blocker and the search engines share
  • desktop: name the waits of the tests
  • wasm: name the waits of the tests
  • media: name the waits of the tests
  • host: name the waits of the tests
  • layout: name the waits of the tests
  • The last interrupt poll named, a stray CR in a comment
  • page: no call deadline on the compile threads
  • frontend: names looked up in a table when a function's lists are long
  • service workers: an opaque response reaches the page with its body
  • csp: a policy applies to inline code from where it stands
  • images: an SVG image's fragment makes :target match
  • frames: a same-origin frame gives navigator, screen and crypto
  • js: the slow script message for microtasks, none for a worker terminated at start
  • style: the search for grouped controls does not go from a shadow root back to its host
  • forms: a submission made while the page loads waits for the embedder
  • framestest: a same-origin frame's navigator is reachable now
  • wrencom_e2e: the launcher's message may wrap
  • getComputedStyle: shorthands and mask-image have values
  • forms: the navigate event of a submission still fires at once
  • timeline: a long animation frame's invoker is a copy
  • getComputedStyle: an unset border radius is 0px
  • src/webaudio: a render graph for the Web Audio API, with no JavaScript in it
  • Web Audio for pages: the interfaces in JavaScript, the graph through api/webaudio.widl
  • Tests of Web Audio: jswebtest pages, a demo to drive, the webaudio folder of web-platform-tests
  • Web Audio in the documentation: a page, the limits in the README and the site, a changelog fragment
  • Web Audio: what a Wasm module of a page could break, a lost device, what test_all found
  • webaudio test: wait for the clock of a resumed context, on a busy machine
  • Release 0.6.23: Web Audio; vscode.dev, tldraw and reddit work; regexp search faster
  • test_all: the desktop unit tests one at a time and marked flaky

Wren 0.6.22

Choose a tag to compare

@github-actions github-actions released this 31 Oct 20:45
v0.6.22
b9bf60a

Wren 0.6.22

Wren 0.6.22 comes to Linux, runs WebAssembly threads and a real crypto.subtle, and stays far smoother on pages that change every frame.

This is still a beta, under development.

Highlights

  • A Linux package. wren-0.6.22-linux-x64.tar.gz for x86-64, an early preview: it needs glibc 2.39 or later (Ubuntu 24.04, Debian 13, Fedora 40). It is built and started on a clean machine before it is published. Keyboard, pointer and a real GPU on Linux were not checked for this release.
  • WebAssembly threads and SharedArrayBuffer under cross-origin isolation. Squoosh encodes an image in a worker.
  • A real crypto.subtle: AES, HMAC, PBKDF2, HKDF, ECDSA and ECDH on P-256 and P-384, RSA, Ed25519 and X25519, in pages and workers. amazon.com renders. P-521 is not there yet.
  • Much smoother with a UI Automation program around. A screenshot tool or PowerToys kept a page's accessibility tree updating on every frame. It now updates ten times a second at most: resizing a 10 000-item flex page went from 6.9 to about 28 frames a second, and a page of 10 000 changing nodes from 1.9 to about 8.
  • A value lost by the garbage collector. What an async function returned could be collected before its promise took it: an ArrayBuffer turned up empty, a rejection reason undefined.
  • More of the web works: blob: URLs for every load, link preload, the common SVG DOM, FontFace, real CSSRule objects, a document for blank frames. Excalidraw and tldraw load.

Added

  • A real crypto.subtle: CryptoKey, importKey / exportKey (raw, jwk, spki, pkcs8), generateKey, encrypt / decrypt (AES-GCM, AES-CBC, AES-CTR, RSA-OAEP), sign / verify (HMAC, ECDSA, RSASSA-PKCS1-v1_5, RSA-PSS, Ed25519), deriveBits / deriveKey (PBKDF2, HKDF, ECDH, X25519), wrapKey / unwrapKey (with AES-KW): in windows and workers, heavy work off the page's thread; a key's material stays in the browser, and a CryptoKey goes through structuredClone, postMessage and IndexedDB. amazon.com stopped at importKey. No P-521, which core:crypto does not have.
  • A Linux package: each release now has wren-<version>-linux-x64.tar.gz (x86-64, an early preview), built by ./build.sh release and tools/package_linux.sh and started end to end on the runner before it is published. The site offers it. The main thread gets its 32 MiB stack by itself on Linux, without a ulimit.
  • A blank same-origin frame has a document: iframe.contentDocument of an about:blank or srcdoc frame is an inert document of the page's own realm (nothing written there shows), and a same-origin frame gives eval and SecurityPolicyViolationEvent exists. A frame with an address of its own stays out of reach: each document has its own engine instance.
  • sheet.cssRules gives real rule objects: CSSStyleRule, CSSMediaRule, CSSLayerBlockRule and the others exist for instanceof, with type, selectorText, style, conditionText, name and nested cssRules (read-only views of the sheet).
  • FontFace: a face loaded from a URL or from bytes and added to document.fonts is in force (Excalidraw and tldraw stopped on its absence).
  • <link rel=preload> and modulepreload fetch and fire load or error, which code loaders wait for (Squoosh's never finished without it).
  • The common SVG DOM: createSVGMatrix, createSVGPoint, createSVGRect, getBBox, getCTM, getScreenCTM; an <svg> with only a viewBox no longer asks for 300 px in a grid track or a shrink-to-fit box.
  • WebAssembly threads and SharedArrayBuffer: under cross-origin isolation (Cross-Origin-Opener-Policy: same-origin with Cross-Origin-Embedder-Policy) a page and its workers share memory, with Atomics.wait and Atomics.notify working with a module's memory.atomic.wait and notify; a WebAssembly.Module and a shared WebAssembly.Memory can be sent to a worker. docs/shared-memory.md.
  • dialog.showModal() is modal: the dialog is above everything, what is outside it does not take the pointer or Tab, Escape asks it to close, the focus goes in and comes back; :modal matches, returnValue can be set, and close is fired from a task.
  • Cross-Origin-Resource-Policy and Cross-Origin-Embedder-Policy are enforced: a response to a request without CORS can refuse other origins, and a page with require-corp or credentialless loads only what agrees.
  • A page's WebAssembly modules are kept compiled in the profile's disk cache, by top-level site.

Changed

  • Pages that change every frame run much faster when a program uses UI Automation. A page's accessibility tree is brought up to date ten times a second at most, and less often when the update is costly (accessibility.page.interval_ms); a screen reader's request is still answered at once. Any program that once asked UI Automation (a screenshot tool, PowerToys) kept that tree alive. Measured: flex 10 000 resize from 6.9 to 28.6 frames a second, a page of 10 000 changing nodes from 1.9 to 8.7. :has() invalidation walks each ancestor once per change.
  • media.audio.backend works on Windows too: null lets audio go by in silence at the pace of the clock. The environment variable WREN_AUDIO_OUTPUT=null does the same whatever the preference, and every test script sets it: the tests no longer make a sound.
  • CHANGELOG.md follows Keep a Changelog 1.1.0: versions are linkable (## [0.6.21] - 2026-10-30, with compare links at the end of the file, written when a version is published), a fragment can also be Deprecated or Security, and tools\changelog.ps1 -Check enforces the form of the file.

Fixed

  • The value an async function returned or threw could be lost, depending on when memory was reclaimed: await then gave an empty buffer, another object, or undefined. It showed in crypto.subtle.exportKey, whose result could come back empty, and as an unhandled rejection with no reason where a script had thrown an error.
  • importScripts refuses a blob: or data: URL whose type is not JavaScript (a blob with no type still runs). Assigning selectorText on a CSS rule no longer throws. :first-child and its family stop at the first sibling met instead of counting the whole family. pointercancel is measured by the event timing.
  • Found on real sites: performance.measure with empty options and an end mark, element names such as _x or :x in createElement, a <track> outside a media element having a track.
  • Streams: for await over a stream follows Web IDL's iterator, tee() passes errors and closes on at once and answers cancel when both branches are cancelled, and it clones every kind of chunk.
  • blob: URLs work everywhere: an image, a stylesheet, a frame or a media element can load what URL.createObjectURL made, not only fetch and XHR of its realm.
  • Structured clone keeps lone surrogates, a resizable ArrayBuffer's maximum and a view's length tracking; a view out of bounds throws instead of reaching a worker as null; an Error without a message has none.
  • waitUntil works for as long as the event has promises pending, and install and activate wait for those added late.
  • A module that throws no longer fires unhandledrejection as well as error, and an import attribute other than type is an error (a SyntaxError in a module, a rejected import()).
  • new Document(), EventSource in a worker (it crashed the browser), syntax errors with their file and line, CSS.escape and IndexedDB names with lone surrogates, IndexedDB's interfaces as plain properties of the global, cssText set to the same value queuing a mutation record, and CSSLayerBlockRule and CSSLayerStatementRule existing.
  • The README, the site and the documentation catch up with the code: the JavaScript engine in Odin (no QuickJS-ng, no Zig), the built-in network stack, text and image decoders, the HTTP cache, the single-file download, the counts of the API and of the bindings, and a list of limits that no longer names what exists (Canvas 2D, video, iframes, service workers, ResizeObserver, Web Animations).
  • The site's gallery: a click on a capture opens it large (arrow keys to go through them, Esc to close), each capture stays a plain link to its image without the script, and the page says which version the captures show (0.6.1, not 0.2.0).
Changes since v0.6.21
  • WebAssembly threads and SharedArrayBuffer under cross-origin isolation
  • blob: URLs for every load, link preload, the common SVG DOM
  • FontFace, and what the big sites stopped on: measure with empty options, element names, a detached track
  • CHANGELOG in the form of Keep a Changelog 1.1.0: linkable versions, six types, a check that enforces it
  • Real CSSRule objects, a document for blank frames, SecurityPolicyViolationEvent
  • A real crypto.subtle: CryptoKey and the other eleven operations
  • Tests make no sound, a Linux package for the releases, flaky steps marked, toolkit 0.57.1
  • Linux package: LLVM 18 on the ubuntu-22.04 runner
  • Linux package: built on ubuntu-24.04, glibc 2.39 or later
  • Linux package: the quick profile of the Rust library, fat LTO does not link
  • Accessibility of pages at most ten updates a second, :has() invalidation walks an ancestor once
  • page: name the bare numbers that carry a decision
  • browser: name the bare numbers that carry a decision
  • net: name the bare numbers that carry a decision
  • fetch: name the bare numbers that carry a decision
  • media: name the bare numbers that carry a decision
  • wasm: name the page limit of script-made memories
  • layout: name the bare numbers that carry a decision
  • style: name the bare numbers that carry a decision
  • page: name the deadlines of offthread_test
  • images: name the codec probe wait and the waits of its test
  • host: name the shutdown defaults and the watchdog tick
  • remote: name the waits of the pipe se...
Read more

Wren 0.6.21

Choose a tag to compare

@github-actions github-actions released this 30 Oct 22:43
v0.6.21
924cda2

Wren 0.6.21

Wren 0.6.21 runs far more of the web's WebAssembly, and starts it faster: DuckDB, Python and older .NET and C++ builds.

This is still a beta, under development, and Windows-only.

Highlights

  • Modules built with the old form of WebAssembly exceptions run. try, catch, rethrow and delegate are what older Emscripten and .NET builds use, and Wasmtime refuses them: they are now translated at compilation. DuckDB's shell (3.8 MB of C++) starts.
  • Python runs. new WebAssembly.Memory({shared: true}) works for one agent, as other browsers allow without isolation, so Pyodide starts and answers runPython.
  • Faster. A script going through a module's memory one element at a time is 3 to 4 times faster, and an instance with thousands of exports takes 104 ms instead of 5.7 s: Pyodide starts in 5.7 s instead of 11.5 s.
  • A module can catch what JavaScript throws (catch_all, WebAssembly.JSTag), a call can have any number of values, and externrefs are released once the module has dropped them.
  • Service workers: event.waitUntil() works from the promise given to respondWith, as the specification allows. bitplatform.dev's Blazor application starts.
  • Streams: releasing a reader during a read no longer reports an unhandled rejection, which stopped DuckDB's worker and failed pipeTo tests.

Changes you may notice

  • Without cross-origin isolation a page has no global SharedArrayBuffer, as in other browsers. Scripts that use it when available no longer wait on a copy that nothing shares: SQLite Fiddle now says OPFS is unavailable.

Limits

  • Still no WebAssembly threads: a module declaring a shared memory does not compile, so Emscripten builds with pthreads do not run. Details: docs/js/webassembly-api.md.

Added

  • WebAssembly modules built with the old form of exceptions run (try, catch, rethrow, delegate: older Emscripten and .NET builds, DuckDB): they are translated at compilation. new WebAssembly.Memory({shared: true}) works for one agent, so Pyodide (Python) starts.

Changed

  • A page's WebAssembly is much faster to start and to reach: a script going through a module's memory one element at a time is 3 to 4 times faster (the engine keeps the pages it reads), and an instance with thousands of exports takes 104 ms instead of 5.7 s (Pyodide starts in 5.7 s instead of 11.5 s).
  • Fewer limits on a page's WebAssembly: a module can catch what JavaScript throws (catch_all, WebAssembly.JSTag), a call can have any number of values, externrefs are released once the module has dropped them, Memory.toFixedLengthBuffer. Without cross-origin isolation a page has no SharedArrayBuffer, as in other browsers: scripts that use it when available no longer wait on a copy that nothing shares (SQLite's OPFS helper). The toolkit moves to 0.56 (timelines and screencast for its development tools; nothing changes in Wren itself).

Fixed

  • A service worker can call event.waitUntil() from the promise it gave respondWith, as the specification allows: Wren refused, so the request failed (bitplatform.dev's Blazor application could not start).
  • A stream's reader released while reading no longer reports an unhandled rejection ("Releasing Default reader"), which stopped a worker in DuckDB's shell and failed pipeTo tests: a promise marked as handled after its rejection now tells the rejection tracker.
Changes since v0.6.20
  • WebAssembly limits: JavaScript's exceptions can be caught by a module, any number of values in a call, externrefs released, no SharedArrayBuffer without isolation
  • WebAssembly: the old form of exceptions translated at compilation
  • test262's image keeps the global SharedArrayBuffer: it measures ECMAScript
  • Release 0.6.21: the memory of a module kept by pages in the engine, instances with thousands of exports, waitUntil from a pending respondWith

Wren 0.6.20

Choose a tag to compare

@github-actions github-actions released this 30 Oct 20:49
v0.6.20
795bd4b

Wren 0.6.20

Wren 0.6.20 runs a page's own WebAssembly: Blazor, Rust and Emscripten applications now start.

This is still a beta, under development, and Windows-only.

Highlights

  • The WebAssembly JavaScript API. WebAssembly.compile, instantiate, the streaming forms, Module, Instance, Memory, Table, Global, Tag and Exception, in windows and workers. A .NET 10 Blazor application that said "This browser does not support WebAssembly" now works, first through its server circuit, then with .NET itself in WebAssembly; a Yew (Rust) application and SQLite Fiddle (Emscripten) start too. On web-platform-tests wasm/jsapi, 82 % of the subtests pass.
  • A large module does not stop the page. WebAssembly.compile works on another thread: for a 34 MB module, the page's timers keep running (worst gap 25 ms) where a synchronous compile blocks for 440 ms.
  • A page that does not touch WebAssembly pays nothing: its objects are made on first use. The page's Content Security Policy applies ('wasm-unsafe-eval').

Limits

  • A script reading a module's memory element by element is slow (186 ns per read, against 15 ns for an ordinary array): the memory is reached through the browser. The module itself runs at Wasmtime's speed, and bulk operations (set, slice, TextDecoder) are one call.
  • No shared memory, so no WebAssembly threads. Modules using the old form of exceptions (older Emscripten and .NET builds) do not compile; a Blazor Web App in Auto mode then stays on its server circuit.
  • javascript.webassembly.enabled turns the API off. Details: docs/js/webassembly-api.md.

Added

  • The WebAssembly JavaScript API, on by default: a page's own modules run on Wasmtime beside the engine's. WebAssembly.validate, compile, instantiate, the streaming forms, Module, Instance, Memory, Table, Global, Tag, Exception and the errors, in windows and workers. compile works on another thread, so a large module does not stop the page. Blazor WebAssembly (.NET 10), wasm-bindgen and Emscripten applications run; the page's Content Security Policy applies ('wasm-unsafe-eval'). No shared memory, so no threads. A page that does not touch WebAssembly pays nothing. javascript.webassembly.enabled turns it off. Details and limits: docs/js/webassembly-api.md.
Changes since v0.6.19
  • WebAssembly JS API, step 1: javascript.webassembly.enabled
  • bindgen outputs of the webassembly module
  • test262 and jsengine shells: the webassembly.WebAssembly_enabled import
  • WebAssembly JS API, step 1: compile, Module
  • Documentation site: the WebAssembly API pages
  • WebAssembly.compile compiles on a thread of the blocking pool: the page goes on meanwhile
  • WebAssembly JS API: Memory, Table, Global, Instance, instantiate, streaming: one store per realm, memory.buffer an ArrayBuffer whose bytes are reached through the browser
  • WebAssembly JS API: Tag, Exception (both ways through calls)
  • Release 0.6.20: the WebAssembly JavaScript API, on by default