Skip to content

The shell

codingncaffeine edited this page Sep 30, 2026 · 27 revisions

The shell

The browser around the engine. It is one OS-free component — tabs, history, the address bar, menus, hit-testing, scrolling, forms, selection, find, reader mode, link hints and devtools — painted through the theme's tokens (Themes) and driven either by a window or by a script (Scripting the shell). The Win32 window blits the shell's frame and feeds it input; the same shell runs headless in CI on all three OSes.

Browsing

  • Tabs — open, close, switch; each keeps its own document, scroll position, controls, selection and find state. A link sent to a tab of its own — Ctrl or the middle button with the click, the link's menu — opens behind the page being read and beside it, after the tabs that page has already opened, so three links opened from one page stand in the order they were opened; once the reader has been to another tab, the next one stands right beside its opener again. A link that asks for a tab of its own (target=_blank), a window a page opens, and Ctrl+Shift with the click come to the front, right beside their opener. A closed tab comes back: Ctrl+Shift+T (or the tab's menu, or the palette) reopens the tab closed last where it stood in the strip, in its container, with its whole history and the place it had scrolled to — drawn from the bytes kept, nothing fetched. The last 25 are kept, as the browsers keep them, and the session file carries them across a restart; a tab that never left the new-tab page is not kept. Duplicate tab opens a copy beside the tab, at the same place in the same history, in the same container. A pinned tab (the tab's menu: Pin tab, Unpin tab) is its icon alone — the page's own, or a sheet of paper for a page that has none — among the strip's first tabs, with no close button: pinning takes a tab to the end of that row and unpinning to the first place after it. "Close other tabs" and "Close tabs to the right" leave pinned tabs standing; one closes when it is asked to itself (its menu, Ctrl+W, the middle button) and comes back pinned; its copy is pinned beside it; what it opens stands after the pinned row; and the session file keeps the pins.
  • History — Back and Forward keep each page's bytes, so going back never refetches.
  • What the shell has to say — where the link under the pointer leads, what is loading, a notice (a page loaded over plain HTTP, a form refused by the page's policy) — floats in a small box over the page's bottom left corner while there is something to say, and is not there otherwise: the page has the window down to its foot, and no status bar is kept. It draws from status-background, status-text and chrome-border (Themes).
  • The command palette — Ctrl+Shift+P opens one list over the page with every command the shell has: a new tab, a new tab in each container, close, reload, back, forward, find, reader mode, the developer tools, the page's source, a switch to each open tab by its title, and each theme preset by name. Typing narrows the list word by word (each word a substring of the label, case aside), the arrow keys move along it and wrap, Enter runs the highlighted command, Escape closes it with nothing done; a click on a row runs it, a click anywhere else closes it.
  • Bookmarks — a tree of folders and bookmarks under two roots, as Firefox and Chrome keep them: the bar's and the rest. Ctrl+D, or the star in the toolbar, puts the page in front on the bar under its title and with its icon — the star is filled while the page is bookmarked, and the same key takes the bookmark away. The bar is the toolbar's second row: the theme's toolbar color and pictures run through it, a moving theme included, its titles are in the theme's toolbar-text, and it is shown while it holds something and the reader's setting says so — always, on the new-tab page only, or never (Ctrl+Shift+B shows and hides it). A bookmark opens in the tab in front, and in a tab of its own for the middle button or with Ctrl; a folder drops its bookmarks as a menu, folders inside it as submenus; the bookmarks that do not fit are behind the chevrons at the bar's right end. A right click gives a bookmark's own menu (open, open in a new tab, edit, remove) and the bar's. The main menu lists them all, and about:bookmarks (Ctrl+Shift+O) is where they are managed, laid out as the managers of Firefox and Chrome are: the folders down the left with what each holds, the folder chosen on the right as a list — icon, name, address — with Edit (a form in the bookmark's place in the list: name, address, folder), Up, Down and Remove; a removal is told at the top of the page with an Undo that puts it back where it stood; a folder is sorted by name (folders first) and opened all at once, each bookmark in a tab behind the page; a search finds a bookmark by its name or address, whatever the letters' case, and says where it is kept; a folder and a bookmark are written in by hand at the list's foot; and when the bar is shown is chosen there. Import and backup is a view of its own: the bookmarks of the other browsers found on the machine — Firefox and LibreWolf from the newest of the compressed copies they keep (bookmarkbackups/*.jsonlz4: a size and one LZ4 block, read here), Chrome, Chromium, Brave, Edge, Vivaldi and Opera from their Bookmarks file, per profile, Flatpak and Snap installs included — each with how many it holds and one click to bring them in (a browser's toolbar onto the bar, the rest into the other bookmarks, nothing replaced); the bookmarks files found in the downloads, desktop, documents and home folders, and a box for any other by its path; Export to bookmarks-<date>.html in the downloads folder, the Netscape bookmarks file every browser reads; and Backups: a dated copy is kept in the profile (bookmark-backups/) on the first start of each day, the last ten days', and one is put back in two steps — the page asks first, and the bookmarks as they stood are themselves kept as a copy before the other takes their place. The machine is looked at only when that view is opened, and only read. What the page's address asks to be done — a removal, an import — is done only when the page itself asked: decided when a load is asked for, so that a page of the web which names such an address in a link, or asks for the page and then for the act, gets the page and nothing more (about:themes keeps the same rule for putting a theme on). They are kept in the profile as bookmarks.json.
  • Containers — a tab in a container has a cookie jar and a localStorage of its own, kept apart from the default container's and from every other's, so a site opened in one never sees what it set in another; a container tab wears its container's colour along its top edge. The profile's containers.json names them in order ([{"name": "Work", "color": "#f59e0b"}, …]; a fresh profile gets Personal, Work, Banking and Shopping). Ctrl+Shift+N opens a tab in the container after the active tab's, round to the default; a link opened in a new tab stays in its container; the session keeps each tab's container. The cache and the connections are shared between containers.
  • The profile — --profile <dir> (see Running Sashfold) holds cookies.txt (the default container's jar in the Netscape format curl and wget read, cookies-<name>.txt for each container's), storage.json (every page's localStorage, per container and origin — a page's storage outlives the page), containers.json and session.json, each written back whenever what it holds changes, at most once a second and once more on the way out, whole or not at all.
  • Session restore — the window writes the session — every tab, each entry of its history with its title and scroll position, which entry each tab is on and which tab is active — to session.json in the profile folder (--profile <dir>, else the platform's configuration directory; see Running Sashfold) whenever it changes, at most once a second and once more on the way out, whole or not at all, so a crash costs a second of it at most. The next start brings the tabs back with their titles and fetches a page the first time its tab is shown (the active one at once), never every tab at start; a URL on the command line opens beside them.
  • Its words — a tab's title, the address, a menu, the palette — are set in a face of the machine's, through the engine's own font stack (Painting and text): the families the theme names (type.font-family, see Themes), else the machine's interface face and its sans-serif, measured and kerned glyph by glyph, with the built-in face last. A page's @font-face fonts take no part in it: the window's words are never a page's to dress. Where the machine's fonts are off, as they are for a script, the built-in face is all there is, and every golden is the same bytes on every OS.
  • The address bar — a caret, selection, Ctrl+A/C/V; a typed host is tried over https first (see Networking); a typed non-URL opens an internal page, never a search engine.
  • Pointing — what the reader points at is what was drawn on top. One lookup, paint::hit_test, walks the page in the painter's own order backwards (CSS 2.1 Appendix E: a stacking context's layers from the top down by z-index, then the lines' words, the floats, the blocks, the layers below zero), asking the painter's own questions of each box, so the two cannot disagree; it serves the page's events, its links, its controls and the inspector alike. A link laid as an empty positioned box over a whole card answers for the card, a block link answers on its padding, a box with pointer-events: none or visibility: hidden is passed through to what it covers, one at no opacity still answers, what a clipping box cuts away is out of reach, and a point on the bare canvas is the root element's. A pointer reports where it is up to a thousand times a second, and each report acted on is such a walk; the window's loop takes in everything that has arrived and, of each run of moves, acts on the last alone (platform::keep_last_pointer_moves — a button, a wheel or a key ends a run, so what is pressed is pressed where the pointer then was), as every browser hands its pages one move a frame.
  • Links — the link is the nearest <a href> at or above what was pointed at, its address resolved against the document base URL (a <base href> moves it); fragment links scroll to their target. A middle click, or a click with Ctrl held, opens a link in a tab of its own; a page hears the keys held with a click (ctrlKey, shiftKey, altKey).
  • Internal pages — about:themes (the reader's themes and the two stores; see Themes), about:sashfold, the error page, the certificate-error page (no override), a plain-text page for text types, an unsupported-content page for the rest, and view-source:.
  • The new-tab page — about:newtab, where a new tab opens with the address bar empty and focused: a clock, a greeting by the hour and the date, over a gradient of the theme's colors or over the theme's own pictures. A theme names a folder beside itself (new-tab.backgrounds, see Themes); each new tab opens on the next picture in name order, and the page fades to the next one every new-tab.rotate milliseconds. The clock ticks on the page's own timers from the minute it opened. Nothing on it touches the network.
  • A page's icon — the last <link rel=icon>, else /favicon.ico, fetched through the page's own loader, decoded (ICO, PNG, GIF, JPEG or BMP) and drawn in the tab before the title.
  • Content blocking — every request the shell makes is judged first by the lists in the blocklists folder (--blocklists <dir>, else blocklists/ beside the executable or its parent; see Security defaults): filter lists in the Adblock Plus syntax EasyList is written in refuse what pages request and hide, through a stylesheet added after the page's own, the elements their ## rules name for the site (the #@# exceptions lift them; the procedural and scriptlet forms are read past), and lists of phishing and malware sites keep the shell off those sites as pages too, with a page saying which list and which line. A refused request costs no connection; no lookup leaves the machine. The lists judge every redirect hop as well, and beside them each page's own Content Security Policy judges what the page fetches, runs and applies (Security defaults); a form whose target the policy refuses says so in the status line.
  • Scrolling — the wheel, Page Up/Down, Home/End, the arrow keys, for the page and for any box that scrolls (see below).

A window that changes size

A window being dragged goes through dozens of sizes a second. The shell takes each size and lays nothing out: a tab remembers the viewport its styles and layout were made for, and is brought up to date when something is about to read it — the tab in front once a frame and before input is answered, the others when they are shown or when their scripts ask where something is. The window's loop acts on the last size of a batch of events. And a new size computes styles only when it can change them: the style set records each @media condition it met and what it came to, so the sheets are read again only when a breakpoint is crossed, and between two the styles are computed again only if one of them measured the side of the viewport that changed (vw, vh, vmin, vmax). A page with neither costs a layout alone. (It once laid out every tab for every size: ninety sizes in a second and a half cost one tab of a news site 25 s of CPU, measured in the real window under a compositor; it is under two now.)

A thread for every tab

Nothing of a page is done by the window's thread. The shell the window calls keeps the chrome — the strip, the toolbar, the bars, its menus, the palette — and of each tab a mirror of what the chrome shows of it: its history's addresses and titles, its icon, its status, whether it is loading, and the page's picture as it was last painted. The page itself lives in an engine: a shell of its own that holds that one tab and draws no chrome, on a thread of its own, where its scripts, styles, layout and paint are done. What the reader does to a page — the pointer, the wheel, keys, a navigation, the find bar's words — is handed to its engine as a job and never waited for; the engine publishes what the page says of itself and its picture, and wakes the window to take them; and what a page asks of the shell — a tab opened, a theme put on, the whole screen — is posted back the same way. What tabs share is behind locks: the storage areas, the IndexedDB stores, the loader with its cookies and its cache. The profile's files are written by a thread of their own as well.

So a page busy for four seconds holds its own tab for four seconds and nothing else: the other tabs run, the strip and the address bar answer, the window moves. Measured in the real window on a YouTube watch page reached by a click: the window's thread was held for 50 of 66 seconds before (in turns of up to 4.2 s), and for none of them after. What is left on the page's side of the line: a scroll waits for the page's thread (Not written yet). A video does not — the next section.

SASHFOLD_ENGINES=0 in the environment gives the window a shell that is whole, as it was, to compare with.

What a turn cost. Every turn of the window's loop is clocked. One of a quarter of a second or more says so on stderr — the journal, for a window started from the desktop — with how many events it took in and how many of them were sizes, and what its load, its scripts and its frame cost: styles, layouts, sheets, pictures, paint, present. --trace-frames (or SASHFOLD_TRACE_FRAMES in the environment) says it for every turn that did anything, and a window that ends itself (--exit-after) says at the end how long its thread was held in all: the turns of 50 ms and of 250 ms and more, their sum and the longest. --drive <file> has the real window take steps by itself on the real clock — a line each, the milliseconds since it opened and then click, click-text, move, wheel, type, key (a chord too: ctrl+t), navigate, text or frame — so that a page is clicked, scrolled and read back in the real window, which a script on its virtual clock cannot show.

A video does not wait for its page

A page that plays a video is busy with everything else as well, and most of all while it starts: on a YouTube watch page the page's scripts, styles and layouts keep its thread for nine of the first twelve seconds, in turns of up to two. A picture that waited for that thread — taken from the decoder and painted by the page — came late or not at all: 5 to 19 pictures a second of a stream that has 30, with waits of up to two seconds, while the sound played on.

So a page on a thread of its own does not paint its videos. Its painting leaves each video's place open: transparent where the video shows, through the same clips as the video would be drawn through — rounded corners included — and whatever the page paints over the video (a player's controls, a caption, a translucent bar) is composited onto the opening with its own alpha. The painting says where the places are. A third thread, the page's compositor, which runs no script and waits for none, takes each picture from the video's decoder the moment it is due, scales it into its place, lays the page's own pixels back over it, and hands the window the page's picture with a finished patch for each video. The two are said together, so a patch is never shown on a painting it was not made for.

The pictures are timed by the element's clock, which the page sets each time it looks at its video and which runs on between its looks: by what has been heard where there are speakers, by the machine's clock where there are none, never past the end of what is buffered. The sound server says where a stream has played to in the blocks its device takes — 21 or 43 ms at a time on a common desktop — and says the same until the next block, so its word is a staircase; pictures shown by it alone come in steps. The clock therefore runs by the machine's own between the server's answers and is set by the answers that catch a step soonest, approached at three parts in a hundred and never jumped to. The speakers hold five seconds of decoded sound and the decoder is handed as many seconds of video, so that both last while the page is kept.

Measured in the real window on that page, with the real sound server: 30 to 33 pictures in every second from the first, the longest wait between two 34 to 40 ms, through stretches of more than two seconds in which the page does not look at its video, and the sound unbroken. Scaling a frame and laying the page over it costs 0.7 ms at 640 by 360, 2.5 ms at 1280 by 720 and 21 ms at 3840 by 2160 — enough there for 30 pictures a second and not for 60, until the frame stays on the graphics card (Not written yet). A video in a frame, a headless render and a shell script paint their own video, as before.

SASHFOLD_MEDIA_TRACE=1 says on stderr, each second, how many pictures were put on the page, the longest wait between two and what composing cost, and explains any wait of a tenth of a second by where the decoder and its clock stood. SASHFOLD_AUDIO_TRACE=1 prints each of the sound server's answers beside the clock. SASHFOLD_MEDIA_SILENT=1 plays every sample as silence through the real device: the real clock, and nothing to hear. SASHFOLD_VIDEO_LAYERS=0 leaves videos to the page's own painting, to compare with.

When a page runs out of memory

One process holds every tab, so a page that allocates without end — its own leak, or a script that misbehaves because something it expects is not written yet — would otherwise stop only at the machine's memory, gigabytes a second, and take the desktop down with it. Two ceilings stand in the way.

A page's script heap has a ceiling (--js-heap-limit, a quarter of the machine's memory between 512 MB and 4 GB). What counts is what is live after a collection — garbage is not the page's fault — and the heap asks its cells their sizes again as it goes (every so many allocations, every so many steps of a script, and whenever a callback returns), because an array grows after it is made. A page found over its ceiling has the running script ended, as a runaway loop is, and no other runs; its console says so once — out of memory: the page's scripts were stopped, its heap over N MB — the page stays as it was drawn, and every other tab, and the next page in the same tab, goes on with a heap of its own.

The process has a ceiling (--memory-ceiling, half the machine's memory between 2 GB and 16 GB) for whatever the first does not see: a thread of its own reads the resident set four times a second, and the first time it is over, the window ends at once: a line on stderr (the journal, for a window started from the desktop) saying how much, then where each page's thread stood and where the main thread did, by function name — which in a runaway is the runaway — then abort. The session file is at most a second old, so the tabs come back at the next start.

Menus

A right click opens the menu of what it landed on, and what is there decides the items, group by group:

  • a link — open it in a new tab, or in a new tab of any container (a submenu), and copy its address; for a link that is not an address to go to (javascript:, mailto:) the two that open it are dimmed;
  • a picture — open it in a new tab, copy its address (the source the page chose from its srcset), and save it to the downloads folder as a download is saved, with the extension its bytes call for when its name has none;
  • a selection — Copy;
  • a field — the click puts the caret in it, and Paste types the clipboard there. Cut and Copy are shown and cannot be chosen yet: a field has a caret and no selection of its own;
  • the page, when none of those is under the pointer — Back, Forward, Reload, Select all, View page source;
  • and Inspect last in every one, which opens the developer tools on the element under the pointer.

The page hears the click first — mousedown, then contextmenu, in the frame's own document when the click is inside a frame — and a page that prevents the default keeps the shell's menu away, which is how a web application puts up a menu of its own. Shift with the click shows the shell's menu regardless.

A tab's menu opens a new tab, reloads that tab, duplicates it, pins or unpins it, closes it, closes the others, or closes those to its right, and reopens the tab closed last. The address bar's has Cut, Copy, Paste, Paste and go, and Select all. The main menu hangs from the button at the toolbar's right end: a new tab, a new tab in any container, find in page, reader mode, the themes with the one in use checked, the command palette, the developer tools, the page's source, the about page, and Quit.

Menus are drawn by the shell like the rest of the chrome, from the theme's popup- tokens (Themes), and stay inside the window. A menu opens with its corner at the pointer, on the pointer's other side where the window leaves no room; a submenu opens beside its item on whichever side has room, and on neither, against the window's edge on the roomier side. An item that cannot be chosen is dimmed, one that is on carries a check mark, one with a submenu an arrow, and the keys that do the same are written at the right.

An open menu holds the pointer and the keyboard. The pointer highlights the item it is over and opens that item's submenu — a move that heads for an open submenu across the rows between leaves it open. A press on an item chooses it; so does letting go of the button that opened the menu, after a slide to the item. A press anywhere else closes the menus and does nothing more, and the wheel moves nothing meanwhile. The Menu key or Shift+F10 opens the menu of whatever has the focus — the address bar, a field, else the page; Up and Down move along it past what cannot be chosen and wrap, Home and End go to its ends, Right opens a submenu and Left closes it, a letter goes to the next item that begins with it, Enter chooses, and Escape closes the innermost menu.

Beside the menus: Ctrl+U shows the page's source, Ctrl+X cuts the address bar's text, and Ctrl+Shift+Q quits.

Boxes that scroll

A box whose overflow is auto, scroll or hidden holds a scrollport its content can be moved within. Layout settles how far each can be moved — the scrollable overflow region past the scrollport's end edges, the end padding included, the walk stopping at a box that scrolls because that box holds its own content — and the shell moves what is inside it.

A wheel notch goes to the innermost box under the pointer that has room to take it; a box with none passes it outward, to the box around it and finally to the page, which is the chaining every browser does. A box that can only move across takes a plain wheel that way, which is what a wide table in a narrow column needs. The keyboard follows whatever the reader last moved by hand. hidden clips and holds a scrollport, but gives a reader no way in at all.

auto shows a scrollbar once there is something out of sight, scroll shows one whether or not there is. The bar is drawn inside the scrollport over the content rather than taking room from it, its thumb as long a part of the track as the scrollport is of the whole content; the thumb can be dragged, and a click on the track beside it moves a scrollport at a time, towards the pointer. A render taken with no shell behind it — --render, and the reference tests — draws no bar at all: nothing has been scrolled there and nothing can be.

The viewport scrolls over everything that can be reached in it, not over the root's box alone: a page whose html and body are height: 100% holds content that runs out of both, and the reader scrolls down to it (what a scroll container holds, or overflow: clip cuts away, is not counted). The overflow the viewport takes from the root or from body is kept: hidden or clip there holds the page still for the wheel, the keys and the bar, while a script's scrollTo still moves a hidden one. Scrolling is applied to the fragments rather than at painting time, so painting, hit-testing, the selection, find-in-page and the devtools overlay all read the coordinates the content is actually drawn at. A link scrolled out of sight is out of reach with it.

Find in page and a fragment link reach into a box: every box between the target and the root is moved until the target is inside each of their scrollports, outermost first — an inner box travels with the outer one, so its own numbers are only settled once the outer one has stopped — and then the page, as before. A target too big for a scrollport is shown from its start edge rather than its end.

What is left: layout reserves no room for a bar, so the bars sit over the content the way an overlay scrollbar does rather than narrowing the scrollport, and a box that sticks does not re-stick as a scrollport is moved.

Frames

A frame takes the wheel, the keyboard, a click, typing and a selection much as the page does, but for itself first. A notch over a frame moves a box inside its document before the document itself, and is passed outward — to the frame around it, and finally to the page — only once each has nowhere left to move; the keyboard follows whichever frame the notch last moved, End and Home included. A frame with more below its own bottom edge draws a thumb down its right edge, as a scrolling box does. A click inside a frame fires in the frame's own realm, at the frame's own coordinates, and a link followed there navigates the frame itself unless its target says otherwise — the frame around it, the page, another frame by name, or a new tab. A field inside a frame takes focus and whatever is typed, as a field on the page does. A selection can begin and grow inside a frame's text, and a drag that crosses the frame's edge carries the selection on past it rather than stopping there.

A document whose body is a frameset is laid out as the grid its rows and cols attributes name (HTML §15.6): each row and column sized from those lists, absolute and percentage entries first, the rest sharing what is left. Between the cells run borders taken out of that space, at the width the border attribute gives, six pixels by default, or none where frameborder says so, in the colour bordercolor names or else gray. A cell holds a frame or another frameset nested inside it, and every frame is drawn live from its own document, the way an iframe is.

Forms, without scripts

Text and password fields, textareas, checkboxes, radio buttons (grouped by name within a form), selects and buttons are drawn, take focus (a click, Tab and Shift+Tab, a label by for or containment), and edit: typing with a caret, Backspace/Delete/Left/Right/Home/End, Space to toggle, Up/Down to pick an option, Enter to submit (a newline in a textarea), Escape to blur. A form submits what the standard's form data set rules make of it (disabled controls, unchecked boxes and non-submitter buttons left out), by its method or its submitter's formmethod: a GET to its action with the data set urlencoded as the query; a POST to its action as written, the data set as the body of the navigation — application/x-www-form-urlencoded unless enctype/formenctype says multipart/form-data (a part a field, between lines of a boundary no field holds) or text/plain — sent with its Content-Type and the Origin of the document that submitted it, never from the cache or into it, a 301, 302 or 303 met on the way followed with a GET and a 307 or 308 as it was. A script's form.submit() and requestSubmit() go the same way, the page's form-action policy is asked first, and a form that posts to what is no web address goes there as a link would, its data left behind. Reloading what a POST brought back asks for its address again with a GET: nothing is sent twice. Author backgrounds and borders replace the built-in look.

Selection and the clipboard

A press on page text sets a caret, a drag extends the selection (held to the content area's edges), Shift with the arrow keys steps by character or by line, Shift+Home/End to the line's ends, Ctrl+A takes everything, Escape drops it. Ctrl+C copies the selected text (or the address bar's) to the operating system's clipboard through CF_UNICODETEXT; Ctrl+V pastes into a field or the address bar. Scripted runs use a clipboard private to the process.

Find in page

Ctrl+F opens a find bar under the toolbar. The count updates as you type ("3 of 12", "No matches"), every match is highlighted with the current one stronger, Enter and Shift+Enter walk them and scroll each into view, Escape closes.

Reader mode

The pilcrow button at the right of the address bar (or the reader: scheme) shows a page's article alone: the container of its paragraphs, found by scoring text length, commas, link density and the names of classes and ids, with the site's heading, the source, absolute links and pictures, and none of the navigation, sidebars or comments. Pressing it again returns to the page.

Keyboard link hints

Pressing f with nothing focused puts a home-row label (asdfghjkl, one letter for up to nine links, two for up to eighty-one) on every link in view. Typing a label follows its link; Backspace takes a letter back; Escape puts the labels away.

Devtools

F12 (or Ctrl+Shift+I) opens a panel under the page: the document's tree on the left, and for the inspected element — a click on the page or on a line of the tree — its summary, its box from the layout, and its computed style on the right, with the box overlaid on the page.

Downloads

Saved under a fresh name in the Downloads folder (--downloads <dir> chooses another), marked with the mark of the web, never opened. See Security defaults.

Testing the shell

The shell is tested by scripts under tests/shell/ that run in CI on Linux, Windows and macOS with chrome screenshots compared byte for byte — see Testing and Scripting the shell. Each script is run twice: by a shell that is whole, and by one whose pages are in engines, called in place on the script's clock; it passes when both do, so what a reader sees is the same either way. The threads themselves are tested by test_page_threads, against the clock: a page that spins for most of a second, a loop that goes on turning meanwhile with no turn waiting for it, and two busy pages done in the time one takes.

Clone this wiki locally