Skip to main content

iced_webview/
lib.rs

1//! A library to embed web views in iced applications.
2//!
3//! Supports [Blitz](https://github.com/DioxusLabs/blitz) (Rust-native, modern CSS),
4//! [litehtml](https://github.com/franzos/litehtml-rs) (lightweight, CPU-based), and
5//! [Servo](https://servo.org/) (full browser: HTML5, CSS3, JS).
6//!
7//! Has two separate widgets: Basic, and Advanced.
8//! The basic widget is simple to implement — use abstractions like `CloseCurrent` and `ChangeView`.
9//! The advanced widget gives you direct `ViewId` control for multiple simultaneous views.
10//!
11//! # Basic usage
12//!
13//! ```rust,ignore
14//! enum Message {
15//!    WebView(iced_webview::Action),
16//!    Update,
17//! }
18//!
19//! struct State {
20//!    webview: iced_webview::WebView<iced_webview::Blitz, Message>,
21//! }
22//! ```
23//!
24//! Then call the usual `view/update` methods — see
25//! [examples](https://github.com/franzos/iced_webview_v2/tree/main/examples) for full working code.
26//!
27use std::sync::atomic::{AtomicU64, Ordering};
28use std::sync::{Arc, OnceLock};
29
30use iced::widget::image;
31
32/// Engine Trait and Engine implementations
33pub mod engines;
34pub use engines::{Engine, PageType, PixelFormat, ViewId};
35
36mod webview;
37pub use basic::{Action, WebView};
38pub use webview::{advanced, basic};
39
40#[cfg(feature = "blitz")]
41pub use engines::blitz::Blitz;
42
43#[cfg(feature = "litehtml")]
44pub use engines::litehtml::Litehtml;
45
46#[cfg(feature = "servo")]
47pub use engines::servo::Servo;
48
49#[cfg(feature = "cef")]
50pub use engines::cef_engine::{cef_subprocess_check, Cef};
51
52pub(crate) mod util;
53
54#[cfg(any(feature = "litehtml", feature = "blitz"))]
55pub(crate) mod fetch;
56
57// Monotonic frame counter; lets the shader pipeline skip re-uploading unchanged pixels.
58static FRAME_GENERATION: AtomicU64 = AtomicU64::new(0);
59
60fn next_generation() -> u64 {
61    FRAME_GENERATION.fetch_add(1, Ordering::Relaxed)
62}
63
64/// A frame's pixel buffer tagged with its generation, for the shader widget path.
65#[cfg_attr(
66    not(any(feature = "servo", feature = "cef", feature = "blitz")),
67    allow(dead_code)
68)]
69#[derive(Clone, Debug)]
70pub struct FramePixels {
71    pub(crate) data: Arc<Vec<u8>>,
72    pub(crate) generation: u64,
73}
74
75/// Image details for passing the view around
76#[derive(Clone, Debug)]
77pub struct ImageInfo {
78    width: u32,
79    height: u32,
80    handle: Arc<OnceLock<image::Handle>>,
81    raw_pixels: Arc<Vec<u8>>,
82    generation: u64,
83}
84
85impl Default for ImageInfo {
86    fn default() -> Self {
87        Self::blank(Self::WIDTH, Self::HEIGHT)
88    }
89}
90
91impl ImageInfo {
92    // The default dimensions
93    const WIDTH: u32 = 800;
94    const HEIGHT: u32 = 800;
95
96    #[allow(dead_code)]
97    fn new(mut pixels: Vec<u8>, format: PixelFormat, width: u32, height: u32) -> Self {
98        // R, G, B, A: drop any trailing partial pixel from engine data.
99        pixels.truncate(pixels.len() / 4 * 4);
100
101        if let PixelFormat::Bgra = format {
102            pixels
103                .as_chunks_mut::<4>()
104                .0
105                .iter_mut()
106                .for_each(|chunk| chunk.swap(0, 2));
107        }
108
109        Self {
110            width,
111            height,
112            handle: Arc::new(OnceLock::new()),
113            raw_pixels: Arc::new(pixels),
114            generation: next_generation(),
115        }
116    }
117
118    /// Get the image handle for direct rendering.
119    ///
120    /// Built lazily on first call: the shader widget path never needs it,
121    /// so engines on that path skip the viewport-sized clone entirely.
122    pub fn as_handle(&self) -> image::Handle {
123        self.handle
124            .get_or_init(|| {
125                image::Handle::from_rgba(self.width, self.height, (*self.raw_pixels).clone())
126            })
127            .clone()
128    }
129
130    /// Image width.
131    pub fn image_width(&self) -> u32 {
132        self.width
133    }
134
135    /// Image height.
136    pub fn image_height(&self) -> u32 {
137        self.height
138    }
139
140    /// Raw RGBA pixel data for direct GPU upload (shader widget path).
141    pub fn pixels(&self) -> FramePixels {
142        FramePixels {
143            data: Arc::clone(&self.raw_pixels),
144            generation: self.generation,
145        }
146    }
147
148    fn blank(width: u32, height: u32) -> Self {
149        // Fall back to 1x1 if the buffer size would overflow usize.
150        let (w, h) = (width as usize)
151            .checked_mul(height as usize)
152            .and_then(|n| n.checked_mul(4))
153            .map_or((1u32, 1u32), |_| (width, height));
154
155        Self {
156            width: w,
157            height: h,
158            handle: Arc::new(OnceLock::new()),
159            raw_pixels: Arc::new(vec![255; w as usize * h as usize * 4]),
160            generation: next_generation(),
161        }
162    }
163}