use std::collections::HashMap;
use std::fmt::{Debug, Error, Formatter};
use crossbeam_channel::Sender;
use embedder_traits::{AnimationState, EventLoopWaker};
use euclid::{Rect, Scale, Size2D};
use log::warn;
use malloc_size_of_derive::MallocSizeOf;
use parking_lot::RwLock;
use rustc_hash::FxHashMap;
use servo_base::Epoch;
use servo_base::id::{PainterId, PipelineId, WebViewId};
use smallvec::SmallVec;
use strum::IntoStaticStr;
use style_traits::CSSPixel;
use surfman::{Adapter, Connection};
use webrender_api::{DocumentId, FontVariation};
pub mod display_list;
pub mod largest_contentful_paint_candidate;
pub mod rendering_context;
pub mod viewport_description;
use std::sync::{Arc, Mutex};
use bitflags::bitflags;
use display_list::PaintDisplayListInfo;
use embedder_traits::ScreenGeometry;
use euclid::default::Size2D as UntypedSize2D;
use profile_traits::mem::{OpaqueSender, ReportsChan};
use serde::{Deserialize, Serialize};
use servo_base::generic_channel::{
self, GenericCallback, GenericReceiver, GenericSender, GenericSharedMemory,
};
pub use webrender_api::ExternalImageSource;
use webrender_api::units::{DevicePixel, LayoutVector2D, TexelRect};
use webrender_api::{
BuiltDisplayList, BuiltDisplayListDescriptor, ExternalImage, ExternalImageData,
ExternalImageHandler, ExternalImageId, ExternalScrollId, FontInstanceFlags, FontInstanceKey,
FontKey, ImageData, ImageDescriptor, ImageKey, NativeFontHandle,
PipelineId as WebRenderPipelineId,
};
use crate::largest_contentful_paint_candidate::LCPCandidate;
use crate::viewport_description::ViewportDescription;
#[derive(Clone)]
pub struct PaintProxy {
pub sender: Sender<Result<PaintMessage, ipc_channel::IpcError>>,
pub cross_process_paint_api: CrossProcessPaintApi,
pub event_loop_waker: Box<dyn EventLoopWaker>,
}
impl OpaqueSender<PaintMessage> for PaintProxy {
fn send(&self, message: PaintMessage) {
PaintProxy::send(self, message)
}
}
impl PaintProxy {
pub fn send(&self, msg: PaintMessage) {
self.route_msg(Ok(msg))
}
pub fn route_msg(&self, msg: Result<PaintMessage, ipc_channel::IpcError>) {
if let Err(err) = self.sender.send(msg) {
warn!("Failed to send response ({:?}).", err);
}
self.event_loop_waker.wake();
}
}
#[derive(Deserialize, IntoStaticStr, Serialize)]
pub enum PaintMessage {
ChangeRunningAnimationsState(WebViewId, PipelineId, AnimationState),
SetFrameTreeForWebView(WebViewId, SendableFrameTree),
SetThrottled(WebViewId, PipelineId, bool),
NewWebRenderFrameReady(PainterId, DocumentId, bool),
PipelineExited(WebViewId, PipelineId, PipelineExitSource),
SendInitialTransaction(WebViewId, WebRenderPipelineId),
ScrollNodeByDelta(
WebViewId,
WebRenderPipelineId,
LayoutVector2D,
ExternalScrollId,
),
ScrollViewportByDelta(WebViewId, LayoutVector2D),
UpdateEpoch {
webview_id: WebViewId,
pipeline_id: PipelineId,
epoch: Epoch,
},
SendDisplayList {
webview_id: WebViewId,
display_list_descriptor: BuiltDisplayListDescriptor,
display_list_info_receiver: GenericReceiver<PaintDisplayListInfo>,
display_list_data_receiver: GenericReceiver<SerializableDisplayListPayload>,
},
GenerateFrame(Vec<PainterId>),
GenerateImageKey(WebViewId, GenericSender<ImageKey>),
GenerateImageKeysForPipeline(WebViewId, PipelineId),
UpdateImages(PainterId, SmallVec<[ImageUpdate; 1]>),
DelayNewFrameForCanvas(WebViewId, PipelineId, Epoch, Vec<ImageKey>),
GenerateFontKeys(
usize,
usize,
GenericSender<(Vec<FontKey>, Vec<FontInstanceKey>)>,
PainterId,
),
AddFont(PainterId, FontKey, Arc<GenericSharedMemory>, u32),
AddSystemFont(PainterId, FontKey, NativeFontHandle),
AddFontInstance(
PainterId,
FontInstanceKey,
FontKey,
f32,
FontInstanceFlags,
Vec<FontVariation>,
),
RemoveFonts(PainterId, Vec<FontKey>, Vec<FontInstanceKey>),
CollectMemoryReport(ReportsChan),
Viewport(WebViewId, ViewportDescription),
ScreenshotReadinessReponse(WebViewId, FxHashMap<PipelineId, Epoch>),
SendLCPCandidate(LCPCandidate, WebViewId, PipelineId, Epoch),
EnableLCPCalculation(WebViewId),
}
impl Debug for PaintMessage {
fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
let string: &'static str = self.into();
write!(formatter, "{string}")
}
}
#[derive(Deserialize, Serialize)]
pub struct SendableFrameTree {
pub pipeline: CompositionPipeline,
pub children: Vec<SendableFrameTree>,
}
#[derive(Clone, Deserialize, Serialize)]
pub struct CompositionPipeline {
pub id: PipelineId,
pub webview_id: WebViewId,
}
#[derive(Serialize, Deserialize)]
pub struct SerializableDisplayListPayload {
#[serde(with = "serde_bytes")]
pub items_data: Vec<u8>,
#[serde(with = "serde_bytes")]
pub cache_data: Vec<u8>,
#[serde(with = "serde_bytes")]
pub spatial_tree: Vec<u8>,
}
#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
pub struct CrossProcessPaintApi(GenericCallback<PaintMessage>);
impl CrossProcessPaintApi {
pub fn new(callback: GenericCallback<PaintMessage>) -> Self {
CrossProcessPaintApi(callback)
}
pub fn dummy() -> Self {
Self::dummy_with_callback(None)
}
pub fn dummy_with_callback(
callback: Option<Box<dyn Fn(PaintMessage) + Send + 'static>>,
) -> Self {
let callback = GenericCallback::new(move |msg| {
if let Some(ref handler) = callback {
if let Ok(paint_message) = msg {
handler(paint_message);
}
}
})
.unwrap();
Self(callback)
}
pub fn send_initial_transaction(&self, webview_id: WebViewId, pipeline: WebRenderPipelineId) {
if let Err(e) = self
.0
.send(PaintMessage::SendInitialTransaction(webview_id, pipeline))
{
warn!("Error sending initial transaction: {}", e);
}
}
pub fn scroll_node_by_delta(
&self,
webview_id: WebViewId,
pipeline_id: WebRenderPipelineId,
delta: LayoutVector2D,
scroll_id: ExternalScrollId,
) {
if let Err(error) = self.0.send(PaintMessage::ScrollNodeByDelta(
webview_id,
pipeline_id,
delta,
scroll_id,
)) {
warn!("Error scrolling node: {error}");
}
}
pub fn scroll_viewport_by_delta(&self, webview_id: WebViewId, delta: LayoutVector2D) {
if let Err(error) = self
.0
.send(PaintMessage::ScrollViewportByDelta(webview_id, delta))
{
warn!("Error scroll viewport: {error}");
}
}
pub fn delay_new_frame_for_canvas(
&self,
webview_id: WebViewId,
pipeline_id: PipelineId,
canvas_epoch: Epoch,
image_keys: Vec<ImageKey>,
) {
if let Err(error) = self.0.send(PaintMessage::DelayNewFrameForCanvas(
webview_id,
pipeline_id,
canvas_epoch,
image_keys,
)) {
warn!("Error delaying frames for canvas image updates {error:?}");
}
}
pub fn update_epoch(&self, webview_id: WebViewId, pipeline_id: PipelineId, epoch: Epoch) {
if let Err(error) = self.0.send(PaintMessage::UpdateEpoch {
webview_id,
pipeline_id,
epoch,
}) {
warn!("Error updating epoch for pipeline: {error:?}");
}
}
#[servo_tracing::instrument(skip_all)]
pub fn send_display_list(
&self,
webview_id: WebViewId,
display_list_info: &PaintDisplayListInfo,
list: BuiltDisplayList,
) {
let (display_list_data, display_list_descriptor) = list.into_data();
let (display_list_data_sender, display_list_data_receiver) =
generic_channel::channel().unwrap();
let (display_list_info_sender, display_list_info_receiver) =
generic_channel::channel().unwrap();
if let Err(e) = self.0.send(PaintMessage::SendDisplayList {
webview_id,
display_list_descriptor,
display_list_info_receiver,
display_list_data_receiver,
}) {
warn!("Error sending display list: {}", e);
}
if let Err(error) = display_list_info_sender.send(display_list_info.clone()) {
warn!("Error sending display list info: {error}. Not sending the rest");
return;
}
let display_list_data = SerializableDisplayListPayload {
items_data: display_list_data.items_data,
cache_data: display_list_data.cache_data,
spatial_tree: display_list_data.spatial_tree,
};
if let Err(error) = display_list_data_sender.send(display_list_data) {
warn!("Error sending display list: {error}");
}
}
pub fn send_lcp_candidate(
&self,
lcp_candidate: LCPCandidate,
webview_id: WebViewId,
pipeline_id: PipelineId,
epoch: Epoch,
) {
if let Err(error) = self.0.send(PaintMessage::SendLCPCandidate(
lcp_candidate,
webview_id,
pipeline_id,
epoch,
)) {
warn!("Error sending LCPCandidate: {error}");
}
}
pub fn generate_frame(&self, painter_ids: Vec<PainterId>) {
if let Err(error) = self.0.send(PaintMessage::GenerateFrame(painter_ids)) {
warn!("Error generating frame: {error}");
}
}
pub fn generate_image_key_blocking(&self, webview_id: WebViewId) -> Option<ImageKey> {
let (sender, receiver) = generic_channel::channel().unwrap();
self.0
.send(PaintMessage::GenerateImageKey(webview_id, sender))
.ok()?;
receiver.recv().ok()
}
pub fn generate_image_key_async(&self, webview_id: WebViewId, pipeline_id: PipelineId) {
if let Err(e) = self.0.send(PaintMessage::GenerateImageKeysForPipeline(
webview_id,
pipeline_id,
)) {
warn!("Could not send image keys to Paint {}", e);
}
}
pub fn add_image(
&self,
key: ImageKey,
descriptor: ImageDescriptor,
data: SerializableImageData,
is_animated_image: bool,
) {
self.update_images(
key.into(),
[ImageUpdate::AddImage(
key,
descriptor,
data,
is_animated_image,
)]
.into(),
);
}
pub fn update_image(
&self,
key: ImageKey,
descriptor: ImageDescriptor,
data: SerializableImageData,
epoch: Option<Epoch>,
) {
self.update_images(
key.into(),
[ImageUpdate::UpdateImage(key, descriptor, data, epoch)].into(),
);
}
pub fn delete_image(&self, key: ImageKey) {
self.update_images(key.into(), [ImageUpdate::DeleteImage(key)].into());
}
pub fn update_images(&self, painter_id: PainterId, updates: SmallVec<[ImageUpdate; 1]>) {
if let Err(e) = self.0.send(PaintMessage::UpdateImages(painter_id, updates)) {
warn!("error sending image updates: {}", e);
}
}
pub fn remove_unused_font_resources(
&self,
painter_id: PainterId,
keys: Vec<FontKey>,
instance_keys: Vec<FontInstanceKey>,
) {
if keys.is_empty() && instance_keys.is_empty() {
return;
}
let _ = self
.0
.send(PaintMessage::RemoveFonts(painter_id, keys, instance_keys));
}
pub fn add_font_instance(
&self,
font_instance_key: FontInstanceKey,
font_key: FontKey,
size: f32,
flags: FontInstanceFlags,
variations: Vec<FontVariation>,
) {
let _x = self.0.send(PaintMessage::AddFontInstance(
font_key.into(),
font_instance_key,
font_key,
size,
flags,
variations,
));
}
pub fn add_font(&self, font_key: FontKey, data: Arc<GenericSharedMemory>, index: u32) {
let _ = self.0.send(PaintMessage::AddFont(
font_key.into(),
font_key,
data,
index,
));
}
pub fn add_system_font(&self, font_key: FontKey, handle: NativeFontHandle) {
let _ = self.0.send(PaintMessage::AddSystemFont(
font_key.into(),
font_key,
handle,
));
}
pub fn fetch_font_keys(
&self,
number_of_font_keys: usize,
number_of_font_instance_keys: usize,
painter_id: PainterId,
) -> (Vec<FontKey>, Vec<FontInstanceKey>) {
let (sender, receiver) = generic_channel::channel().expect("Could not create IPC channel");
let _ = self.0.send(PaintMessage::GenerateFontKeys(
number_of_font_keys,
number_of_font_instance_keys,
sender,
painter_id,
));
receiver.recv().unwrap()
}
pub fn viewport(&self, webview_id: WebViewId, description: ViewportDescription) {
let _ = self.0.send(PaintMessage::Viewport(webview_id, description));
}
pub fn pipeline_exited(
&self,
webview_id: WebViewId,
pipeline_id: PipelineId,
source: PipelineExitSource,
) {
let _ = self.0.send(PaintMessage::PipelineExited(
webview_id,
pipeline_id,
source,
));
}
}
#[derive(Clone)]
pub struct PainterSurfmanDetails {
pub connection: Connection,
pub adapter: Adapter,
}
#[derive(Clone, Default)]
pub struct PainterSurfmanDetailsMap(Arc<Mutex<HashMap<PainterId, PainterSurfmanDetails>>>);
impl PainterSurfmanDetailsMap {
pub fn get(&self, painter_id: PainterId) -> Option<PainterSurfmanDetails> {
let map = self.0.lock().expect("poisoned");
map.get(&painter_id).cloned()
}
pub fn insert(&self, painter_id: PainterId, details: PainterSurfmanDetails) {
let mut map = self.0.lock().expect("poisoned");
let existing = map.insert(painter_id, details);
assert!(existing.is_none())
}
pub fn remove(&self, painter_id: PainterId) {
let mut map = self.0.lock().expect("poisoned");
let details = map.remove(&painter_id);
assert!(details.is_some());
}
}
pub trait WebRenderExternalImageApi {
fn lock(&mut self, id: u64) -> (ExternalImageSource<'_>, UntypedSize2D<i32>);
fn unlock(&mut self, id: u64);
}
#[derive(Clone, Copy)]
pub enum WebRenderImageHandlerType {
WebGl,
Media,
WebGpu,
}
#[derive(Default)]
struct WebRenderExternalImageIdManagerInner {
external_images: FxHashMap<ExternalImageId, WebRenderImageHandlerType>,
next_image_id: u64,
}
#[derive(Default, Clone)]
pub struct WebRenderExternalImageIdManager(Arc<RwLock<WebRenderExternalImageIdManagerInner>>);
impl WebRenderExternalImageIdManager {
pub fn next_id(&mut self, handler_type: WebRenderImageHandlerType) -> ExternalImageId {
let mut inner = self.0.write();
inner.next_image_id += 1;
let key = ExternalImageId(inner.next_image_id);
inner.external_images.insert(key, handler_type);
key
}
pub fn remove(&mut self, key: &ExternalImageId) {
self.0.write().external_images.remove(key);
}
pub fn get(&self, key: &ExternalImageId) -> Option<WebRenderImageHandlerType> {
self.0.read().external_images.get(key).cloned()
}
}
pub struct WebRenderExternalImageHandlers {
webgl_handler: Option<Box<dyn WebRenderExternalImageApi>>,
media_handler: Option<Box<dyn WebRenderExternalImageApi>>,
webgpu_handler: Option<Box<dyn WebRenderExternalImageApi>>,
id_manager: WebRenderExternalImageIdManager,
}
impl WebRenderExternalImageHandlers {
pub fn new(id_manager: WebRenderExternalImageIdManager) -> Self {
Self {
webgl_handler: Default::default(),
media_handler: Default::default(),
webgpu_handler: Default::default(),
id_manager,
}
}
pub fn id_manager(&self) -> WebRenderExternalImageIdManager {
self.id_manager.clone()
}
pub fn set_handler(
&mut self,
handler: Box<dyn WebRenderExternalImageApi>,
handler_type: WebRenderImageHandlerType,
) {
match handler_type {
WebRenderImageHandlerType::WebGl => self.webgl_handler = Some(handler),
WebRenderImageHandlerType::Media => self.media_handler = Some(handler),
WebRenderImageHandlerType::WebGpu => self.webgpu_handler = Some(handler),
}
}
}
impl ExternalImageHandler for WebRenderExternalImageHandlers {
fn lock(
&mut self,
key: ExternalImageId,
_channel_index: u8,
_is_composited: bool,
) -> ExternalImage<'_> {
let handler_type = self
.id_manager()
.get(&key)
.expect("Tried to get unknown external image");
match handler_type {
WebRenderImageHandlerType::WebGl => {
let (source, size) = self.webgl_handler.as_mut().unwrap().lock(key.0);
let texture_id = match source {
ExternalImageSource::NativeTexture(b) => b,
_ => panic!("Wrong type"),
};
ExternalImage {
uv: TexelRect::new(0.0, size.height as f32, size.width as f32, 0.0),
source: ExternalImageSource::NativeTexture(texture_id),
}
},
WebRenderImageHandlerType::Media => {
let (source, size) = self.media_handler.as_mut().unwrap().lock(key.0);
let texture_id = match source {
ExternalImageSource::NativeTexture(b) => b,
_ => panic!("Wrong type"),
};
ExternalImage {
uv: TexelRect::new(0.0, size.height as f32, size.width as f32, 0.0),
source: ExternalImageSource::NativeTexture(texture_id),
}
},
WebRenderImageHandlerType::WebGpu => {
let (source, size) = self.webgpu_handler.as_mut().unwrap().lock(key.0);
ExternalImage {
uv: TexelRect::new(0.0, size.height as f32, size.width as f32, 0.0),
source,
}
},
}
}
fn unlock(&mut self, key: ExternalImageId, _channel_index: u8) {
let handler_type = self
.id_manager()
.get(&key)
.expect("Tried to get unknown external image");
match handler_type {
WebRenderImageHandlerType::WebGl => self.webgl_handler.as_mut().unwrap().unlock(key.0),
WebRenderImageHandlerType::Media => self.media_handler.as_mut().unwrap().unlock(key.0),
WebRenderImageHandlerType::WebGpu => {
self.webgpu_handler.as_mut().unwrap().unlock(key.0)
},
};
}
}
#[derive(Deserialize, Serialize)]
pub enum ImageUpdate {
AddImage(
ImageKey,
ImageDescriptor,
SerializableImageData,
bool,
),
DeleteImage(ImageKey),
UpdateImage(
ImageKey,
ImageDescriptor,
SerializableImageData,
Option<Epoch>,
),
UpdateImageForAnimation(ImageKey, ImageDescriptor),
}
impl Debug for ImageUpdate {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::AddImage(image_key, image_desc, _, is_animated_image) => f
.debug_tuple("AddImage")
.field(image_key)
.field(image_desc)
.field(is_animated_image)
.finish(),
Self::DeleteImage(image_key) => f.debug_tuple("DeleteImage").field(image_key).finish(),
Self::UpdateImage(image_key, image_desc, _, epoch) => f
.debug_tuple("UpdateImage")
.field(image_key)
.field(image_desc)
.field(epoch)
.finish(),
Self::UpdateImageForAnimation(image_key, image_desc) => f
.debug_tuple("UpdateAnimation")
.field(image_key)
.field(image_desc)
.finish(),
}
}
}
#[derive(Debug, Deserialize, Serialize)]
pub enum SerializableImageData {
Raw(GenericSharedMemory),
External(ExternalImageData),
}
impl From<SerializableImageData> for ImageData {
fn from(value: SerializableImageData) -> Self {
match value {
SerializableImageData::Raw(shared_memory) => ImageData::new(shared_memory.to_vec()),
SerializableImageData::External(image) => ImageData::External(image),
}
}
}
pub trait WebViewTrait {
fn id(&self) -> WebViewId;
fn screen_geometry(&self) -> Option<ScreenGeometry>;
fn set_animating(&self, new_value: bool);
}
#[derive(Clone, Copy, Default, Deserialize, PartialEq, Serialize)]
pub struct PipelineExitSource(u8);
bitflags! {
impl PipelineExitSource: u8 {
const Script = 1 << 0;
const Constellation = 1 << 1;
}
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct PinchZoomInfos {
pub zoom_factor: Scale<f32, DevicePixel, DevicePixel>,
pub rect: Rect<f32, CSSPixel>,
}
impl PinchZoomInfos {
pub fn new_from_viewport_size(size: Size2D<f32, CSSPixel>) -> Self {
Self {
zoom_factor: Scale::identity(),
rect: Rect::from_size(size),
}
}
}