mirror of
https://github.com/Hopiu/bowser.git
synced 2026-03-16 19:10:24 +00:00
No description
| .github | ||
| assets | ||
| src | ||
| tests | ||
| .gitignore | ||
| main.py | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
Bowser — Educational Web Browser
A custom web browser built from scratch following the browser.engineering curriculum. Features a clean architecture with Skia-based rendering, GTK 4/Adwaita UI, and proper separation of concerns.
Status: Milestone 3 - Basic HTML rendering with text layout and image support
Features
- Adwaita Tab Bar - Modern GNOME-style tab management
- Skia Rendering - Hardware-accelerated 2D graphics
- Text Layout - Word wrapping, character-level selection
- Image Support - Load and render images from HTTP, data URLs, and local files
- DOM Parsing - HTML parsing with proper tree structure
- Debug Mode - Visual layout debugging with FPS counter
- DOM Visualization - Generate visual graphs of page structure
Quick Start
# Install dependencies and run
uv sync
uv run bowser
# Browse a website
uv run bowser example.com
# Enable debug mode (shows FPS, layout boxes)
uv run bowser example.com --debug
Keyboard Shortcuts
| Shortcut | Action |
|---|---|
Ctrl+T |
New tab |
Ctrl+W |
Close tab |
Ctrl+L |
Focus address bar |
Ctrl+Shift+D |
Generate DOM visualization |
Ctrl+Shift+O |
Toggle debug mode |
Architecture
bowser/
├── src/
│ ├── browser/ # Browser UI (chrome.py, tab.py)
│ │ ├── browser.py # Application orchestration
│ │ ├── chrome.py # GTK/Adwaita window, tab bar, address bar
│ │ └── tab.py # Tab and frame management
│ │
│ ├── parser/ # Document parsing
│ │ ├── html.py # HTML → DOM tree (Element, Text nodes)
│ │ └── css.py # CSS parsing (stub)
│ │
│ ├── layout/ # Layout calculation
│ │ ├── document.py # DocumentLayout - full page layout
│ │ ├── block.py # BlockLayout, LineLayout - block elements
│ │ ├── inline.py # TextLayout, InlineLayout - text runs
│ │ └── embed.py # ImageLayout - embedded content
│ │
│ ├── render/ # Painting & rendering
│ │ ├── pipeline.py # RenderPipeline - coordinates layout/paint
│ │ ├── fonts.py # FontCache - Skia font management
│ │ ├── paint.py # DisplayList, DrawText, DrawRect, DrawImage
│ │ └── composite.py # Layer compositing
│ │
│ ├── network/ # Networking
│ │ ├── http.py # HTTP client with redirects
│ │ ├── url.py # URL parsing and normalization
│ │ ├── cookies.py # Cookie management
│ │ ├── images.py # Image loading and caching
│ │ └── tasks.py # Async task queue for background loading
│ │
│ ├── debug/ # Development tools
│ │ └── dom_graph.py # DOM tree visualization
│ │
│ └── templates.py # Page templates (start page, errors)
│
├── tests/ # Test suite
└── main.py # Entry point
Design Principles
Separation of Concerns:
parser/- Pure DOM data structures (Element, Text)layout/- Position and size calculations (x, y, width, height)render/- Drawing commands and font managementbrowser/- UI only (tabs, address bar, event handling)
Key Classes:
| Class | Package | Responsibility |
|---|---|---|
Element, Text |
parser | DOM tree nodes |
DocumentLayout |
layout | Page layout with line positioning |
LayoutLine, LayoutBlock |
layout | Positioned text with bounding boxes |
ImageLayout, LayoutImage |
layout | Image sizing and positioning |
RenderPipeline |
render | Coordinates layout → paint |
DrawImage |
render | Image rendering command |
FontCache |
render | Skia font caching |
ImageCache |
network | Image loading and caching |
Chrome |
browser | GTK window, delegates to RenderPipeline |
Development
Prerequisites
- Python 3.11+
- GTK 4 (
libgtk-4-dev libgtk-4-1) - libadwaita (
libadwaita-1-dev) - Graphviz (optional, for DOM visualization)
# Debian/Ubuntu
sudo apt install libgtk-4-dev libadwaita-1-dev graphviz
Testing
uv run pytest # Run all tests
uv run pytest -v # Verbose output
uv run pytest --cov=src # Coverage report
Code Quality
uv run black src tests # Format code
uv run ruff check src # Lint
uv run mypy src # Type check
Debug Mode
Enable with --debug flag or press Ctrl+Shift+O at runtime.
Shows:
- FPS counter with frame timing breakdown
- Layout boxes colored by element type:
- Red: Block elements (
<div>,<p>) - Blue: Inline elements (
<span>,<a>) - Green: List items (
<li>)
- Red: Block elements (
Milestones
- M0: Project scaffold
- M1: GTK window with Skia rendering
- M2: HTML parsing and text layout
- M3: Image loading and rendering
- M4: CSS parsing and styling
- M5: Clickable links and navigation
- M6: Form input and submission
- M7: JavaScript execution
- M8: Event handling
Image Support
Bowser supports loading and rendering images from multiple sources:
Supported Sources
- HTTP/HTTPS URLs:
<img src="https://example.com/image.png"> - Data URLs:
<img src="data:image/png;base64,..."> - Local files:
<img src="file:///path/to/image.png">
Features
- Async loading: Images load in background threads, keeping UI responsive
- Smart sizing: Respects width/height attributes, maintains aspect ratios
- Caching: Thread-safe global image cache prevents redundant loads
- Alt text placeholders: Shows placeholder with alt text when images fail
- Format support: PNG, JPEG, GIF, WebP, and more (via Skia)
- Viewport culling: Only renders visible images for performance
- Progressive display: Page shows immediately, images appear as they load
Example
<!-- Basic image -->
<img src="photo.jpg">
<!-- Sized image with aspect ratio -->
<img src="photo.jpg" width="300">
<!-- With alt text for accessibility -->
<img src="photo.jpg" alt="A beautiful sunset">
<!-- Data URL (embedded image) -->
<img src="data:image/png;base64,iVBORw0KG...">
Architecture
HTML <img> tag
↓
ImageLayout.load(async=True)
↓
TaskQueue (background thread pool)
↓
load_image() → HTTP/file/data URL
↓ ↓
ImageCache GLib.idle_add
(thread-safe) ↓
on_complete callback
↓
ImageLayout.image = loaded
↓
RenderPipeline._request_redraw()
↓
DrawImage.execute() → Canvas
References
- Web Browser Engineering — O'Reilly book
- Let's Build a Browser Engine — Matt Brubeck
License
MIT