oxichrome

repository·main·Indexed 43 days ago

https://github.com/0xsouravm/oxichrome

A framework for writing Chrome and Firefox browser extensions entirely in Rust, compiled to WebAssembly. It leverages Leptos for reactive UI and provides typed bindings to Chrome APIs (storage, tabs, and runtime) to eliminate manual JavaScript. Includes the cargo-oxichrome CLI for scaffolding, building, and managing extensions.

Tokens
6.2K
Snippets
33
Records
38
Agent score
53%

What's inside oxichrome

  1. Build oxichrome extensions for Chromium or Firefox

    main

    Use cargo oxichrome build to compile your Rust code into WebAssembly and generate the necessary extension files (manifest, JS/HTML shims, and assets).

    • Chromium (Chrome, Edge, Brave): Run cargo oxichrome build. Output is placed in dist/chromium/.
    • Firefox: Run cargo oxichrome build --target firefox. Output is placed in dist/firefox/.
    • Release: Use --release for an optimized build.

    Loading the extension:

    • Chromium: Load the dist/chromium/ directory in chrome://extensions using "Load unpacked".
    • Firefox: Load the dist/firefox/manifest.json in about:debugging#/runtime/this-firefox.
    # Chrome/Edge/Brave
    cargo oxichrome build
    
    # Firefox
    cargo oxichrome build --target firefox
  2. Install cargo-oxichrome

    main

    To use oxichrome, install the cargo-oxichrome CLI tool. The build process will automatically handle the installation of the wasm32-unknown-unknown target and the wasm-bindgen-cli (matching the required version) on your first build.

    Prerequisites:

    • Rust (stable)
    • wasm32-unknown-unknown target (auto-installed)
    • wasm-bindgen-cli (auto-installed)
    cargo install cargo-oxichrome
  3. Quick start with oxichrome

    main

    Scaffold a new extension project, navigate to the directory, and build it using the following steps:

    cargo oxichrome new my-extension
    cd my-extension
    cargo oxichrome build

    cargo oxichrome new creates a project with oxichrome, wasm-bindgen, and serde already configured in your Cargo.toml dependencies.

  4. Load the built extension into a browser

    main

    Once the build is complete, follow these steps to load your extension:

    For Chromium-based browsers (Chrome, Brave, etc.)

    1. Locate the dist/chromium/ folder.
    2. Open your browser's extension management page.
    3. Enable "Developer mode".
    4. Select "Load unpacked" and choose the dist/chromium/ folder.

    For Firefox

    1. Locate the dist/firefox/ folder.
    2. Open Firefox and navigate to about:debugging.
    3. Select "This Firefox" from the sidebar.
    4. Click "Load Temporary Add-on..." and select a file within the dist/firefox/ folder.
  5. Build an oxichrome extension

    main

    The build command automates the process of compiling a Rust crate to WebAssembly, running wasm-bindgen, generating the browser extension manifest, and creating the necessary JavaScript shims for background scripts, popups, options pages, and content scripts.

    Build Process Workflow

    1. Prerequisite Check: Ensures the wasm32-unknown-unknown target is installed via rustup and that the correct version of wasm-bindgen-cli is available.
    2. Compilation: Runs cargo build targeting wasm32-unknown-unknown.
    3. WASM Processing: Runs wasm-bindgen to generate web-compatible WASM and JS glue code.
    4. Manifest & Shims Generation: Parses src/lib.rs to generate manifest.json and the required .js files (e.g., background.js, popup.js, options.js, and content script shims).
    5. Asset Management: Copies files from a static/ directory in your crate root to the distribution folder.
    6. Optimization: If wasm-opt is found in your PATH, it performs an -Oz optimization on the WASM output.

    Output Locations

    The build artifacts are placed in a dist/ directory within your crate root, organized by browser type:

    • Chromium: dist/chromium/
    • Firefox: dist/firefox/
  6. Access Chrome APIs in Rust

    main

    Oxichrome provides typed bindings to core Chrome APIs. Common modules include:

    Storage

    • oxichrome::storage::get(key): Retrieves a value.
    • oxichrome::storage::set(key, value): Sets a value.
    • oxichrome::storage::remove(key): Removes a value.

    Tabs

    • oxichrome::tabs::query(query): Queries tabs.
    • oxichrome::tabs::create(props): Creates a new tab.
    • oxichrome::tabs::send_message(tab_id, msg): Sends a message to a tab.

    Runtime

    • oxichrome::runtime::get_url(path): Gets the full URL for a resource.
    • oxichrome::runtime::send_message(msg): Sends a message to the runtime.
    // Storage example
    let val: Option<i32> = oxichrome::storage::get("key").await?;
    oxichrome::storage::set("key", &42).await?;
    oxichrome::storage::remove("key").await?;
    
    // Tabs example
    let tabs: Vec<Tab> = oxichrome::tabs::query(&query).await?;
    let tab: Tab = oxichrome::tabs::create(&props).await?;
    oxichrome::tabs::send_message(tab_id, &msg).await?;
    
    // Runtime example
    let url = oxichrome::runtime::get_url("icon.png");
    oxichrome::runtime::send_message(&msg).await?;
  7. Use oxichrome CLI commands

    main

    The cargo oxichrome command provides the following interface:

    CommandDescription
    cargo oxichrome buildBuild for Chromium (default)
    cargo oxichrome build --releaseOptimized release build
    cargo oxichrome build --target firefoxBuild for Firefox
    cargo oxichrome cleanRemove the dist/ directory
    cargo oxichrome new <name>Scaffold a new extension project
  8. Create a new tab using `create`

    main

    The create function creates a new tab based on the provided properties. It takes a serializable properties object P and returns a Result<T>, where T is the deserialized representation of the newly created tab. This wraps the chrome.tabs.create JavaScript API.

    // Example usage (assuming a TabCreateProperties struct is defined)
    let props = TabCreateProperties { url: Some("https://example.com".to_string()), ..Default::default() };
    let new_tab: Tab = create(&props).await?;
  9. Handle Oxichrome runtime errors with OxichromeError

    main

    The OxichromeError enum is the primary error type used by the oxichrome runtime. It encapsulates errors originating from different layers of the system, specifically JavaScript interop and serialization. You can use the Result<T> type alias for functions that return an OxichromeError on failure.

    Supported error variants:

    • Js(JsError): Errors originating from the JavaScript environment. These are automatically converted from wasm_bindgen::JsValue.
    • Serde(String): Errors occurring during serialization or deserialization via serde_wasm_bindgen.
    • UnexpectedValue(String): Errors indicating that a value did not match the expected type or format.
    use oxichrome_core::error::Result;
    
    fn my_function() -> Result<() {
        // ... logic that might return OxichromeError
        Ok(())
    }
  10. Persist and retrieve data using the storage API

    main

    The storage module provides asynchronous, type-safe bindings to the chrome.storage.local API. It uses serde for automatic serialization and deserialization of Rust types to and from JavaScript values. This allows you to store complex structs and enums directly in the browser's local storage.

    // Example: Storing and retrieving a custom struct
    #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq)]
    struct UserSettings {
        theme: String,
        font_size: u32,
    }
    
    async fn manage_settings() -> oxichrome::Result<()> {
        let settings = UserSettings {
            theme: "dark".to_string(),
            font_size: 14,
        };
    
        // Save to storage
        storage::set("user_prefs", &settings).await?;
    
        // Retrieve from storage
        if let Some(retrieved) = storage::get::<UserSettings>("user_prefs").await? {
            println!("Loaded settings: {:?}", retrieved);
        }
    
        // Remove from storage
        storage::remove("user_prefs").await?;
    
        Ok(())
    }
  11. Send messages to the extension runtime with send_message

    main

    The send_message function allows you to send a serializable message to the Chrome runtime. It uses serde to convert your Rust data structure into a JavaScript value before passing it through the JS bridge. This function is asynchronous and returns a Result<JsValue> containing the response from the receiver.

    #[derive(serde::Serialize)]
    struct MyMessage {
        command: String,
        payload: i32,
    }
    
    async fn notify_runtime() -> oxichrome_core::Result<()> {
        let msg = MyMessage { command: "ping".to_string(), payload: 42 };
        let response = oxichrome_core::send_message(&msg).await?;
        println!("Received: {:?}", response);
        Ok(())
    }