Version 0.2: Browser API - Implementation Plan
November 22, 2025 · View on GitHub
Status: ✅ Complete
Feature: Browser launching, contexts, and page lifecycle
User Story: As a Rust developer, I want to launch browsers and create page objects so that I can prepare for browser automation (navigation and interaction come in Version 0.3).
Related ADRs:
Approach: Vertical slicing with TDD (Red → Green → Refactor), following Version 0.1 pattern
Progress: 7/7 slices complete (100%)
Overview
Version 0.2 builds on Version 0.1's protocol foundation to implement browser launching and page lifecycle management. This enables users to:
- Launch browsers (Chromium, Firefox, WebKit)
- Create browser contexts (isolated sessions)
- Create page objects (empty pages at about:blank)
- Basic lifecycle management (close browsers/contexts/pages)
Note: Navigation (page.goto()), element interaction (clicks, typing), and locators are Version 0.3. Version 0.2 only creates the browser/context/page objects.
Prerequisites from Version 0.1
✅ Protocol foundation complete:
- JSON-RPC communication working
- Object factory and ChannelOwner pattern established
- Connection lifecycle management
- Playwright initialization flow
- Access to browser types
Note: Items deferred from Version 0.1 have been moved to Version 0.3 - see v0.3-page-interactions.md
Proposed Scope
Core Features
-
BrowserType::launch() - Launch browser process
- Launch options (headless, args, etc.)
- Browser object creation
- Browser lifecycle management
-
Browser object - Represents browser instance
new_context()- Create browser contextnew_page()- Shortcut for default context + pageclose()- Graceful shutdowncontexts()- List contexts- Events: close
-
BrowserContext object - Isolated browser session
new_page()- Create page in contextclose()- Close all pagespages()- List pages- Events: page, close
-
Page object - Web page instance (initially at about:blank)
close()- Close pageurl()- Get current URL (returns "about:blank" initially)is_closed()- Check if page is closed- Events: close
Note: Navigation (goto()), content (title(), content()), and interactions are Version 0.3.
Documentation
- Rustdoc for all public APIs
- Examples for common patterns
- Migration guide from Version 0.1
Testing
- Unit tests for each object type
- Integration tests with real browser launching
- Cross-browser tests (all three browsers)
- Error handling tests
Out of Scope (Future Versions)
- Version 0.3: Navigation (
page.goto()), locators (page.locator()), actions (click, type, fill) - Version 0.4: Screenshots, network interception, assertions, content APIs
- Version 0.5: Mobile emulation, advanced features
Version 0.2 is strictly about object lifecycle - creating and closing Browser/Context/Page objects. The actual web automation (navigation, interaction) comes in Version 0.3.
Open Questions
- How to handle async Drop for Browser/Page? (Same pattern as Playwright from Version 0.1?)
- Launch options API design - builder pattern or options struct?
- Should we implement Drop for Browser/Context/Page for auto-cleanup?
Success Criteria
- Can launch all three browsers (Chromium, Firefox, WebKit)
- Can create browser contexts
- Can create pages
- Can close browsers gracefully
- All tests passing with real browsers (macOS, Linux)
- Documentation complete
- Example code works
Note: Windows CI support deferred to Version 0.3 - see v0.3-page-interactions.md
Implementation Slices
Slice 1: Browser Object Foundation ✅
Goal: Create Browser protocol object that can be instantiated from server messages
Tasks:
- Create
protocol/browser.rswith Browser struct - Implement ChannelOwner trait for Browser
- Add Browser to object factory type dispatch
- Parse initializer (version, name fields)
- Integration test (compile-time verification)
Files:
- New:
crates/playwright-core/src/protocol/browser.rs - Modify:
crates/playwright-core/src/object_factory.rs - Modify:
crates/playwright-core/src/protocol/mod.rs - New:
crates/playwright-core/tests/browser_creation.rs
Tests:
#[test]
fn test_browser_type_exists() {
// Verifies Browser implements ChannelOwner
// Passes: ✅
}
Definition of Done:
- ✅ Browser struct exists
- ✅ Can be created from
__create__message - ✅ Registered in object factory
- ✅ Compile-time test passes
Slice 2: Launch Options API ✅
Goal: Create LaunchOptions with builder pattern and normalization
Tasks:
- Create
api/launch_options.rswith full option set - Implement builder pattern methods
- Implement normalize() for protocol compatibility
- Unit tests for builder and normalization
Files:
- New:
crates/playwright-core/src/api/launch_options.rs - New:
crates/playwright-core/src/api/mod.rs - Modify:
crates/playwright-core/src/lib.rs
Tests:
#[test]
fn test_launch_options_builder()
#[test]
fn test_launch_options_normalize_env()
#[test]
fn test_launch_options_normalize_ignore_default_args()
Definition of Done:
- ✅ LaunchOptions has all 17+ fields
- ✅ Builder pattern works
- ✅ Normalization matches protocol format
- ✅ Unit tests pass (7 tests)
Slice 3: BrowserType::launch() ✅
Goal: Implement browser launching with real server integration
Tasks:
- Add
launch()method to BrowserType - Add
launch_with_options()method - Handle launch RPC via Channel
- Parse response and retrieve Browser from registry
- Integration test with real browser launch
Files:
- Modified:
crates/playwright-core/src/protocol/browser_type.rs - New:
crates/playwright-core/tests/browser_launch_integration.rs
Tests:
#[tokio::test]
async fn test_launch_chromium() // ✅ Passing
#[tokio::test]
async fn test_launch_with_headless_option() // ✅ Passing
#[tokio::test]
async fn test_launch_all_three_browsers() // ✅ Passing
Definition of Done:
- ✅ Can launch Chromium with default options
- ✅ Can launch with custom options
- ✅ Can launch Firefox and WebKit
- ✅ Browser object accessible after launch
- ✅ Integration tests pass with real browsers
Key Implementation Details:
- Used
Channel::send()for "launch" RPC call LaunchOptions::normalize()converts options to protocol format- Response contains
{ browser: { guid: "..." } } - Retrieved Browser from connection registry via
get_object() - Downcast Arc
to Browser using as_any()
Gotchas Discovered:
- ⚠️ Browser versions must match Playwright server version
- Server 1.49.0 requires:
npx playwright@1.49.0 install - Documented in README.md to prevent "Executable doesn't exist" errors
- Updated CI workflow to install matching browsers automatically
CI Updates:
- Added browser installation step:
npx playwright@1.49.0 install chromium firefox webkit --with-deps - Windows: Uses
--with-depsflag only on Linux/macOS (Windows runners have deps pre-installed) - Added browser caching to speed up CI runs
- Cache key:
${{ runner.os }}-playwright-browsers-1.49.0
Windows Workaround (Version 0.1 Known Issue):
- ⚠️ Integration tests hang on Windows (stdio pipe cleanup issue)
- CI now runs unit tests only on Windows:
cargo test --lib --workspace - macOS/Linux run full test suite:
cargo test --workspace - Will be fixed when implementing proper cleanup (Browser::close() or Drop implementation)
Slice 4: Browser::close() ✅
Goal: Implement graceful browser shutdown
Tasks:
- Add
close()method to Browser - Send "close" RPC to server
- Handle server cleanup response
- Test close with real browser
- Update existing tests to use close()
Files:
- Modified:
crates/playwright-core/src/protocol/browser.rs - Modified:
crates/playwright-core/tests/browser_launch_integration.rs - Modified:
crates/playwright-core/tests/browser_creation.rs
Tests:
#[tokio::test]
async fn test_browser_close() // ✅ Passing
#[tokio::test]
async fn test_close_multiple_browsers() // ✅ Passing
Definition of Done:
- ✅ Can close browser gracefully
- ✅ Server process terminates cleanly
- ✅ Tests verify cleanup works
- ✅ All existing tests updated to use close()
Key Implementation Details:
- Simple RPC call:
channel.send_no_result("close", json!({})) - No response payload needed (void return)
- Works across multiple browsers
- All 7 tests pass with proper cleanup
Slice 5: BrowserContext Object ✅
Goal: Create BrowserContext protocol object
Tasks:
- Create
protocol/browser_context.rs - Implement ChannelOwner for BrowserContext
- Add to object factory
- Implement
Browser::new_context() - Integration test
Files:
- New:
crates/playwright-core/src/protocol/browser_context.rs - Modified:
crates/playwright-core/src/protocol/browser.rs - Modified:
crates/playwright-core/src/protocol/mod.rs - Modified:
crates/playwright-core/src/object_factory.rs - New:
crates/playwright-core/tests/browser_context_integration.rs
Tests:
#[tokio::test]
async fn test_new_context() // ✅ Passing
#[tokio::test]
async fn test_multiple_contexts() // ✅ Passing
Definition of Done:
- ✅ BrowserContext object exists
- ✅ Can create context from browser
- ✅ Can create multiple contexts
- ✅ Can close contexts
- ✅ Tests pass
Key Implementation Details:
- Used
Channel::send()for "newContext" RPC call - Response contains
{ context: { guid: "..." } } - Retrieved BrowserContext from connection registry via
get_object() - Downcast Arc
to BrowserContext using as_any() - Started with minimal options (empty JSON) - full ContextOptions deferred to later slice if needed
- BrowserContext implements
close()method for cleanup
Note: Full ContextOptions API (viewport, user agent, etc.) was deferred. Version 0.2 focuses on basic object lifecycle. Context configuration options can be added in a future slice or phase if needed.
Slice 6: Page Object ✅
Goal: Create Page protocol object with basic methods
Tasks:
- Create
protocol/page.rs - Implement ChannelOwner for Page
- Add to object factory
- Implement
BrowserContext::new_page() - Implement
Browser::new_page()(convenience) - Add basic page methods:
url(),close() - Integration tests
- Update examples
Files:
- New:
crates/playwright-core/src/protocol/page.rs - Modified:
crates/playwright-core/src/protocol/browser_context.rs - Modified:
crates/playwright-core/src/protocol/browser.rs - Modified:
crates/playwright-core/src/protocol/mod.rs - Modified:
crates/playwright-core/src/object_factory.rs - New:
crates/playwright-core/tests/page_integration.rs - New:
crates/playwright/examples/browser_lifecycle.rs - Modified:
crates/playwright/examples/basic.rs
Tests:
#[tokio::test]
async fn test_context_new_page() // ✅ Passing
#[tokio::test]
async fn test_browser_new_page_convenience() // ✅ Passing
#[tokio::test]
async fn test_multiple_pages_in_context() // ✅ Passing
#[tokio::test]
async fn test_page_close() // ✅ Passing
Definition of Done:
- ✅ Page object exists
- ✅ Can create page from context
- ✅ Can create page from browser (convenience)
- ✅ Basic page methods work (
url(),close()) - ✅ Tests pass
- ✅ Examples updated
Key Implementation Details:
- Used
Channel::send()for "newPage" RPC call - Response contains
{ page: { guid: "..." } } - Retrieved Page from connection registry via
get_object() - Downcast Arc
to Page using as_any() url()returns "about:blank" for Version 0.2 (URL tracking in Version 0.3)Browser::new_page()convenience creates default context internally- Created comprehensive examples demonstrating browser lifecycle
Note: Full URL tracking will be implemented in Version 0.3 when navigation and events are added. For now, page.url() always returns "about:blank".
Slice 7: Cleanup and Documentation ✅
Goal: Finalize Version 0.2 with docs and examples
Tasks:
- Update public API exports in
playwrightcrate - Verify rustdoc for all public APIs
- Fix rustdoc link warnings
- Update README with Version 0.2 features
- Run full test suite
- Verify examples run successfully
- Review and implement deferred TODOs from Version 0.2
- Update Version 0.2 status to Complete
Files:
- Modified:
crates/playwright/src/lib.rs - Modified:
crates/playwright-core/src/protocol/browser.rs(doc fix) - Modified:
crates/playwright-core/src/protocol/page.rs(doc fix) - Modified:
crates/playwright-core/tests/connection_integration.rs(implemented deferred tests) - Modified:
README.md - Modified:
docs/roadmap.md - Modified:
CLAUDE.md - Modified:
docs/implementation-plans/v0.2-browser-api.md
Tests:
- ✅ All tests pass
- ✅ Both examples run successfully
- ✅ Doc tests compile without warnings
Definition of Done:
- ✅ All APIs documented
- ✅ Examples demonstrate browser lifecycle
- ✅ All tests pass (macOS, Linux)
- ✅ Version 0.2 marked complete
- ✅ Public API exports updated
- Note: Windows integration tests deferred (tracked in Success Criteria and "Deferred from Version 0.1" section)
Key Accomplishments:
- Exported all Version 0.2 types:
Browser,BrowserContext,Page,BrowserType,LaunchOptions - Updated main lib.rs example to show full Version 0.2 workflow
- Fixed all rustdoc warnings
- Implemented deferred tests from connection_integration.rs:
test_concurrent_requests_with_server- verifies concurrent operations across Browser/Context/Pagetest_error_response_from_server- verifies protocol error handling
- Established documentation hierarchy and just-in-time principles
- Verified complete test coverage
- Both examples running cleanly
Version 0.2 Complete! 🎉
All 7 slices successfully implemented:
- ✅ Slice 1: Browser Object Foundation
- ✅ Slice 2: Launch Options API
- ✅ Slice 3: BrowserType::launch()
- ✅ Slice 4: Browser::close()
- ✅ Slice 5: BrowserContext Object
- ✅ Slice 6: Page Object
- ✅ Slice 7: Cleanup and Documentation
Next Steps
Version 0.3: Page Interactions - Navigation, locators, and actions
page.goto()- Navigate to URLspage.locator()- Find elements with auto-waiting- Actions:
click(),fill(),type(), etc. - Content queries:
text_content(),inner_html(), etc.
See Roadmap for full Version 0.3 scope.
Created: 2025-11-06 Last Updated: 2025-11-07 Completed: 2025-11-07
Timeline:
- Slice 1-7: All completed 2025-11-07
- Total development time: 2 days