github lightpanda-io/browser 1.0.3

7 hours ago

Behavior changes

  • serve: the --http-max-concurrent default now follows --cdp-max-connections (8× it, between 40 and 256). With the default 16 connections it is 128.
  • fetch --dump markdown now asks the server for markdown (Accept: text/markdown,...). A text/markdown response is dumped as-is. Not sent with --dump-selector, --wait-selector or --wait-script.
  • Accessibility.getPartialAXTree now honors fetchRelatives, as Chrome does: it returns the node and its relatives, no longer the whole subtree. Use getFullAXTree or getChildAXNodes for a subtree.
  • DOM.querySelector with no match now returns nodeId: 0 instead of an error, as Chrome does.
  • Touch event handlers (ontouchstart, …) are removed from document and elements: Lightpanda doesn't emulate a touch device.
  • navigator.sendBeacon now checks its arguments. Beacons are still not sent unless --load-resources beacon is set.
  • The HTTP cache database is deleted and rebuilt when its schema version changes.
  • waitForState now reports a timeout instead of always answering that the page reached the state.
  • MCP over HTTP: a request on a session closed with DELETE now gets a 404 instead of starting a new session.
  • fetch() and new Request() with a body on GET or HEAD now throw a TypeError.
  • Scripts inserted from an isolated world (Playwright setContent, appendChild) now run in the main world.
  • Event listeners now run in their own world's context, so Playwright's focus() and selectOption() fire React's onFocus/onChange.

New Web APIs

  • AudioContext (WAV only)
  • Payment Request API: PaymentRequest, PaymentResponse and their events, behaving like Chrome with no payment app installed
  • NavigateEvent on navigation (intercept() is not implemented)
  • PerformanceNavigationTiming entry for the document
  • document.execCommand: insertText, delete, forwardDelete, selectAll, insertLineBreak, and the styleWithCSS, useCSS and defaultParagraphSeparator settings. Other commands return false
  • image.decode()
  • HTMLElement.isContentEditable (real value), HTMLElement.spellcheck, document.designMode
  • Touch constructor and TouchEvent touch lists
  • elementFromPoint/elementsFromPoint on ShadowRoot
  • SVG getBBox()
  • HTMLFrameElement, SecurityPolicyViolationEvent, a stub Path2D
  • navigator.mimeTypes (empty MimeTypeArray)
  • img.sizes; link.relList.supports() for dns-prefetch, preconnect and prefetch
  • HTMLFieldSetElement.elements; abbr, mark, small, wbr and other HTMLElement-only tags are now HTMLElement instead of HTMLUnknownElement
  • onmouseenter and onmouseleave handlers
  • Web Crypto: raw-secret and raw-public key formats; CryptoKey is now serializable (structuredClone, postMessage, IndexedDB)
  • MessagePort is now transferable
  • TextDecoderStream decodes every encoding TextDecoder supports, with state kept across chunks
  • XMLSerializer now writes XML instead of HTML
  • WebMCP follows the current spec: document.modelContext, getTools(), toolchange, registerTool returns a promise
  • CSS: native nesting, color parsing, more known properties (CSS.supports, unknown property errors)

Web API behavior / fixes

  • Layout: flex-shrink, horizontal margins, left/right/inset, and widths from text measurement. Widths are relative to the containing box. Out-of-flow children no longer size their parent, and block boxes start at their parent's left edge
  • position is now read from stylesheets, not only from the inline style (offsetParent, getComputedStyle)
  • An iframe without size is now 300×150, and its box is the frame's viewport, so Playwright can click inside it
  • Range.getClientRects() now returns one rect per selected element or text node
  • elementFromPoint returns null outside the viewport
  • Textarea scrollHeight now counts its text, for auto-sizing textareas
  • Typing and Input.insertText now insert text into contenteditable elements and designMode documents; Ctrl-A selects all
  • Selectors: :in-range/:out-of-range, :default, :scope on a document; :required/:optional only on form controls; [attr^=""], [attr$=""] and [attr*=""] match nothing
  • Forms: stepMismatch, Chrome-like type=email validation (with multiple), readonly fields skip validation, empty action means the document's URL
  • margin, padding, inset and border-* shorthands now expand into longhands
  • style.x = null and setProperty(name, null) now remove the declaration
  • Media queries accept fractional lengths (max-width: 767.98px)
  • Changing a <style>'s text now clears the style cache
  • Un-rendered light DOM (slot fallback with assigned content, unslotted children) is now treated as hidden
  • A closed <details> keeps its loose text out of innerText
  • <a> and <area> URL setters keep userinfo and reject invalid values; better URL port handling
  • navigation.navigate() with a fragment now updates location and the entry URL
  • dataset rejects invalid names and maps a leading capital to data--
  • isEqualNode compares namespace URIs
  • DOMTokenList.forEach sees mutations made by its callback
  • select/selectionchange events are now dispatched asynchronously; eventPhase is AT_TARGET in direct dispatch
  • <dialog>: show() on an open dialog does nothing, close fires asynchronously
  • xhr.responseType = 'document' now parses HTML into an HTMLDocument
  • innerHTML = "" on an <html> element creates <head> and <body>
  • document.fonts.load() no longer fires load events when there are no font faces
  • navigator.appVersion now starts with 5.0
  • Properties moved to the right interfaces (e.g. title on Document); the global's prototype is now Window.prototype
  • new WebSocket(url, ['a', 'a']) throws a SyntaxError
  • A string longer than V8 accepts throws a RangeError instead of aborting
  • import() of a module that threw rejects with the module's exception
  • Inline scripts report errors at their position in the document
  • Timers run on the initial about:blank page
  • Pending timer cap raised from 8192 to 65536

Scripts

  • The parser runs microtasks before preparing a <script>, so a MutationObserver can change its type first
  • Dynamic scripts with async=false run in order; dynamic scripts can't be defer; inline modules run like external ones
  • Already-fetched async scripts now run from a task, after the markup that follows them
  • <script src=""> fires error instead of fetching the page

Cookies / HTTP

  • A non-secure origin can't set or overwrite a Secure cookie (RFC 6265bis)
  • The cookie jar evicts old cookies past Chrome's limits instead of failing the response
  • A cookie with neither a name nor a value is ignored; IP addresses are each their own site
  • The HTTP cache skips Range requests and URLs with userinfo; Vary matching fixed
  • --http-cache-set-cookie=skip|store|strip added (experimental, store and strip need --experimental-features)
  • DNS lookups are shared across connections
  • The robots.txt cache now expires after 24 hours
  • Web Bot Auth: Signature-Agent is the quoted origin, and each signed request has a nonce

CDP

  • File downloads (Browser.setDownloadBehavior), saved to disk or streamed
  • Accessibility domain completed: getRootAXNode, getAXNodeAndAncestors, getChildAXNodes; shadow DOM is now walked in the AX tree
  • Page.setDocumentContent (Puppeteer page.setContent())
  • Performance.getMetrics (Timestamp, JSHeapUsedSize, JSHeapTotalSize)
  • Input.setInterceptDrags and Input.dispatchDragEvent
  • CSS.getComputedStyleForNode
  • DOM.getContentQuads and DOM.getBoxModel for text nodes
  • Page.navigateToHistoryEntry restores a same-document entry in place
  • Fetch.fulfillRequest now stores the response's cookies
  • Network.loadingFinished.encodedDataLength now reports the bytes received
  • An unknown context returns Chrome's error code and message
  • /json/protocol updated

Tools / MCP

  • download tool, streamed to disk
  • pageTools and callPageTool: list and call the page's WebMCP tools
  • classify, acceptCookies and login tools (TypeSafe Jev with TYPESAFE_API_KEY)
  • MCP run tool: run a PandaScript by source or by path
  • MCP stdio: notifications/cancelled stops the in-flight tool call
  • tree: ARIA checked and expanded states
  • interactiveElements reports whether an element is in a fixed or sticky overlay
  • Structured data: microdata
  • A missing required argument is now named in the error; null means unset
  • A backendNodeId whose node left the document is rejected
  • Markdown: empty code spans dropped, no inline markup inside code
  • Redacted dumps keep an empty password field empty

Agent

  • xAI (Grok) provider, XAI_API_KEY
  • LLMD provider and Parallel search (PARALLEL_API_KEY)
  • REPL tool commands take a prompt (/click the add to cart button), recorded with explicit selectors for /save
  • The REPL exits when its terminal goes away
  • Ctrl-C and timeouts now stop a script that loops after an await

WebDriver

  • POST /session/{id}/window

Performance / stability

  • Compiled scripts (classic and modules) are stored in the HTTP cache
  • Single-threaded arena allocator (~10% faster on WPT)
  • Faster V8 ↔ Zig calls (~10%)
  • Faster style matching, selector lists, class and sibling selectors, getTag()
  • Inline transforms no longer drop the cascade's memos
  • Cookie jar stored per site
  • Arenas free blocks they can't keep instead of keeping them in place
  • Response bodies are no longer buffered in the transfer's arena
  • Faster startup on macOS
  • Crash and UAF fixes: call_arena in nested calls, EventSource double abort, document.write, structured-cloned FileList, DataCloneError messages, WebMCP tool errors, stale tool registry nodes

Security

  • brotli updated to 1.2.1

Build

  • Nix package and dev shell

Don't miss a new browser release

NewReleases is sending notifications on new releases.