Ladybird Browser

repository·master·Indexed 13 days ago

https://github.com/ladybirdbrowser/ladybird

An independent web browser built from scratch using a novel engine based on web standards. Currently in pre-alpha for developer use, the project includes documentation on its general architecture, LibWeb components, build instructions for Linux, macOS, and Windows, and specialized tools for randomized testing and fuzzing.

Tokens
70.4K
Snippets
160
Records
312
Agent score
98%

What's inside Ladybird

  1. Omnibox presentation and UI rendering

    master

    The Omnibox popup renders an ordered suggestion list with the following visual characteristics:

    • Row Limit: Displays at most six rows before scrolling.
    • Row Layout: Rows with a title use two lines; URL-only and search rows use one line.
    • Icons:
      • Search rows use a search icon.
      • Local navigation rows use a favicon (or a globe if unavailable).
      • Bookmarks receive a star badge.
    • Text Highlighting: Matched portions of titles and URLs use match ranges from LibWebView. Highlighting uses both weight and brightness to ensure visibility in both dark and light themes.
  2. Identify Ladybird coding conventions

    master

    Ladybird follows specific coding patterns to ensure fidelity to web standards:

    • Spec Fidelity: Implement web-platform features exactly according to the steps in the official specification algorithms.
    • Verbatim Comments: Use abundant code comments containing verbatim text copied from the specification to show exactly what is being implemented.
    • AD-HOC Convention: Use the "AD-HOC:" comment prefix to mark code that does not map to any specific specification requirements.
    • Naming Alignment: Class and file names are designed to closely match current specification terms (e.g., Navigable.h, Transferable.h).
  3. Understand the Ladybird codebase and AK library

    master

    Ladybird is written primarily in C++ and uses the internal AK library instead of the standard C++ STL.

    Because many AK and internal library facilities are not extensively documented, you should learn by:

    • Inspecting header files.
    • Examining existing code for usage examples.

    Key Developer Documentation:

  4. How automatic selection and inline completion differ

    master

    Automatic selection and inline completion are separate decisions. A result can be displayed at the top without being safe to select or complete.

    Automatic Selection

    Determines what happens when the user presses Enter without selecting a specific row.

    • Eligible candidates: Sufficiently long URL prefixes (backed by direct visits), bookmark URL prefixes (min 2 chars), or meeting adaptive thresholds.
    • Ineligible: Title-only, folder, substring, previous-search, and remote suggestion matches.
    • Hysteresis: To prevent selection churn during incremental refreshes, a new candidate must exceed the current one by both 10% and 100 relevance points.

    Inline Completion

    Stricter than automatic selection. A candidate must:

    1. Be eligible for automatic selection.
    2. Be a URL-prefix match.
    3. Extend the user's input at the end of the editor.
    4. Produce a non-empty suffix (after handling scheme/www.).
    5. Not contain a query string (unless an exact adaptive association permits it).

    Note: Title, folder, substring, search, and literal URL candidates never inline-complete.

  5. Handle multiple completions with `wire-batch:`

    master

    The wire-batch: line is emitted when more than one request completes within a single curl multi tick.

    When you see RequestServer wire-batch: drained N completions in one curl multi tick followed by several wire: lines with identical timestamp prefixes, the clustering is cosmetic. The requests actually completed at different times, but they were all processed in the same loop.

    To reconstruct true per-request completion times: Take the drain timestamp from the batch line and subtract each request's drain delay (found in the wire^: line) from its wire^: timestamp.

    RequestServer wire-batch: drained 3 completions in one curl multi tick
  6. Use RefPtr and NonnullRefPtr for shared ownership

    master

    Use RefPtr<T> for objects with multiple owners. Shared ownership is managed via reference counting. An object is deleted only when the last RefPtr pointing to it is destroyed.

    NonnullRefPtr<T> is a variant that guarantees the pointer is never null.

    Requirements for RefPtr: To be compatible with RefPtr, a class T must implement ref() and unref(). The easiest way to achieve this is by inheriting from RefCounted<T>:

    class Bar : public RefCounted<Bar> {
        ...
    };
    • To convert a known non-null RefPtr to a NonnullRefPtr, use RefPtr::release_nonnull() or dereference it with operator*.
    • A NonnullRefPtr can be assigned to a RefPtr, but not vice versa.
  7. Implement fallible constructors using `create` static methods

    master

    Standard C++ constructors cannot return ErrorOr<T>. To handle fallible initialization, define a static function (conventionally named create) that returns ErrorOr<NonnullOwnPtr<T>> or ErrorOr<T>.

    This method should:

    1. Prepare arguments and perform fallible operations.
    2. Initialize the object using a private constructor.
    3. Perform any post-initialization fallible setup.
    4. Return the resulting object wrapped in ErrorOr.
    class Decompressor {
    public:
        static ErrorOr<NonnullOwnPtr<Decompressor>> create(NonnullOwnPtr<Core::Stream::Stream> stream)
        {
            auto buffer = TRY(CircularBuffer::create_empty(32 * KiB));
            auto decompressor = TRY(adopt_nonnull_own_or_enomem(new (nothrow) Decompressor(move(stream), move(buffer))));
            TRY(decompressor->initialize_settings_from_header());
            return decompressor;
        }
    
    private:
        Decompressor(NonnullOwnPtr<Core::Stream::Stream> stream, CircularBuffer buffer)
            : m_stream(move(stream)), m_buffer(move(buffer)) {}
    
        CircularBuffer m_buffer;
        NonnullOwnPtr<Core::Stream::Stream> m_stream;
    };
  8. Understand the LibWeb three-layer wrapper architecture

    master

    LibWeb uses a three-layer model to expose C++ implementation objects to JavaScript, ensuring that the same implementation object can be observed through different realms or isolated worlds without losing identity within a specific world.

    1. Web::Bindings::Wrappable: The C++ base class for any implementation object that can be reflected into JavaScript.
    2. PlatformObject wrappers: Generated by the WebIDL generator in Web::Bindings. These wrappers hold the actual implementation object and implement the Web-facing behavior.
    3. Web::Bindings::WrapperWorld: Owns the wrapper identity for a single observable world. Wrapping an object involves looking up or creating the wrapper within the caller's WrapperWorld.

    This architecture results in exactly one wrapper identity per (implementation object, WrapperWorld) pair.

  9. Understand the two types of Ladybird ports

    master

    Ladybird ports are categorized into two distinct layers:

    1. UI Ports: These focus on the "browser frontend" or UI layer. This includes the browser window, tabs, address bar, and other visual elements. Currently supported UI ports are Qt6 (generic) and AppKit/Cocoa (macOS native).

    2. Platform Ports: These focus on the underlying platform-specific code that interacts with the Operating System, such as file I/O, networking, and process management. Currently supported platform ports are GNU/Linux and macOS. An Android port is currently in progress.

  10. Understand LibCore's EventLoop system

    master

    LibCore's EventLoop is a cooperative multitasking scheduler used for handling application tasks concurrently on a single thread. It is an in-process loop that processes incoming events (signals, notifiers, file watchers, timers, etc.) by executing associated callbacks.

    Key characteristics:

    • Thread-Local: Event loops are managed via thread-local variables. EventLoop::current() returns the topmost event loop on the calling thread.
    • Event Loop Stack: Supports nesting (e.g., for GUI windows). When a nested event loop exits, its pending events are returned to the queue of the lower event loop in the stack.
    • Cooperative: It relies on callbacks eventually returning control to the loop so other events can be processed.
    • Not Web Event Loops: This system is distinct from the LibWeb event loops used in browser contexts.
  11. Understand the OutOfProcessWebView and WebContent IPC

    master

    In the GUI application, the OutOfProcessWebView widget manages the lifecycle and communication of helper processes. The relationship between the GUI and the web content is structured as follows:

    1. OutOfProcessWebView: A widget in the GUI process that spawns helper processes.
    2. WebContentClient: An object inside OutOfProcessWebView that implements the client side of the WebContent IPC (Inter-Process Communication) protocol.
    3. WebContent::ConnectionFromClient: The corresponding object inside the WebContent process that receives IPC calls.
    4. WebContent::PageHost: Hosted by the connection, it manages the LibWeb engine's main Web::Page object.

    LibWeb Object Hierarchy

    Inside the WebContent process, the engine follows this hierarchy:

    • Web::Page: The top-level engine object.
    • Web::Frame: The main frame (and subframes for <frame> or <iframe> elements).
    • Web::Document: The root node of the DOM tree within a frame.