use std::fmt::{Debug, Error, Formatter};
use base::id::{PipelineId, WebViewId};
use crossbeam_channel::Sender;
use embedder_traits::{
AnimationState, EventLoopWaker, MouseButton, MouseButtonAction, TouchEventResult,
};
use euclid::Rect;
use ipc_channel::ipc::IpcSender;
use log::warn;
use pixels::Image;
use strum_macros::IntoStaticStr;
use style_traits::CSSPixel;
use webrender_api::DocumentId;
pub mod display_list;
pub mod rendering_context;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use display_list::CompositorDisplayListInfo;
use embedder_traits::{CompositorHitTestResult, ScreenGeometry};
use euclid::default::Size2D as UntypedSize2D;
use ipc_channel::ipc::{self, IpcSharedMemory};
use profile_traits::mem::{OpaqueSender, ReportsChan};
use serde::{Deserialize, Serialize};
use servo_geometry::{DeviceIndependentIntRect, DeviceIndependentIntSize};
use webrender_api::units::{DevicePoint, LayoutPoint, TexelRect};
use webrender_api::{
BuiltDisplayList, BuiltDisplayListDescriptor, ExternalImage, ExternalImageData,
ExternalImageHandler, ExternalImageId, ExternalImageSource, ExternalScrollId,
FontInstanceFlags, FontInstanceKey, FontKey, HitTestFlags, ImageData, ImageDescriptor,
ImageKey, NativeFontHandle, PipelineId as WebRenderPipelineId,
};
#[derive(Clone)]
pub struct CompositorProxy {
pub sender: Sender<CompositorMsg>,
pub cross_process_compositor_api: CrossProcessCompositorApi,
pub event_loop_waker: Box<dyn EventLoopWaker>,
}
impl OpaqueSender<CompositorMsg> for CompositorProxy {
fn send(&self, message: CompositorMsg) {
CompositorProxy::send(self, message)
}
}
impl CompositorProxy {
pub fn send(&self, msg: CompositorMsg) {
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 CompositorMsg {
ChangeRunningAnimationsState(WebViewId, PipelineId, AnimationState),
CreateOrUpdateWebView(SendableFrameTree),
RemoveWebView(WebViewId),
TouchEventProcessed(WebViewId, TouchEventResult),
CreatePng(
WebViewId,
Option<Rect<f32, CSSPixel>>,
IpcSender<Option<Image>>,
),
IsReadyToSaveImageReply(bool),
SetThrottled(WebViewId, PipelineId, bool),
NewWebRenderFrameReady(DocumentId, bool),
PipelineExited(WebViewId, PipelineId, IpcSender<()>),
LoadComplete(WebViewId),
WebDriverMouseButtonEvent(WebViewId, MouseButtonAction, MouseButton, f32, f32),
WebDriverMouseMoveEvent(WebViewId, f32, f32),
SendInitialTransaction(WebRenderPipelineId),
SendScrollNode(
WebViewId,
WebRenderPipelineId,
LayoutPoint,
ExternalScrollId,
),
SendDisplayList {
webview_id: WebViewId,
display_list_descriptor: BuiltDisplayListDescriptor,
display_list_receiver: ipc::IpcBytesReceiver,
},
HitTest(
Option<WebRenderPipelineId>,
DevicePoint,
HitTestFlags,
IpcSender<Vec<CompositorHitTestResult>>,
),
GenerateImageKey(IpcSender<ImageKey>),
AddImage(ImageKey, ImageDescriptor, SerializableImageData),
UpdateImages(Vec<ImageUpdate>),
GenerateFontKeys(
usize,
usize,
IpcSender<(Vec<FontKey>, Vec<FontInstanceKey>)>,
),
AddFont(FontKey, Arc<IpcSharedMemory>, u32),
AddSystemFont(FontKey, NativeFontHandle),
AddFontInstance(FontInstanceKey, FontKey, f32, FontInstanceFlags),
RemoveFonts(Vec<FontKey>, Vec<FontInstanceKey>),
GetClientWindowRect(WebViewId, IpcSender<DeviceIndependentIntRect>),
GetScreenSize(WebViewId, IpcSender<DeviceIndependentIntSize>),
GetAvailableScreenSize(WebViewId, IpcSender<DeviceIndependentIntSize>),
CollectMemoryReport(ReportsChan),
}
impl Debug for CompositorMsg {
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(Clone, Deserialize, Serialize)]
pub struct CrossProcessCompositorApi(pub IpcSender<CompositorMsg>);
impl CrossProcessCompositorApi {
pub fn dummy() -> Self {
let (sender, _) = ipc::channel().unwrap();
Self(sender)
}
pub fn sender(&self) -> &IpcSender<CompositorMsg> {
&self.0
}
pub fn send_initial_transaction(&self, pipeline: WebRenderPipelineId) {
if let Err(e) = self.0.send(CompositorMsg::SendInitialTransaction(pipeline)) {
warn!("Error sending initial transaction: {}", e);
}
}
pub fn send_scroll_node(
&self,
webview_id: WebViewId,
pipeline_id: WebRenderPipelineId,
point: LayoutPoint,
scroll_id: ExternalScrollId,
) {
if let Err(e) = self.0.send(CompositorMsg::SendScrollNode(
webview_id,
pipeline_id,
point,
scroll_id,
)) {
warn!("Error sending scroll node: {}", e);
}
}
pub fn send_display_list(
&self,
webview_id: WebViewId,
display_list_info: CompositorDisplayListInfo,
list: BuiltDisplayList,
) {
let (display_list_data, display_list_descriptor) = list.into_data();
let (display_list_sender, display_list_receiver) = ipc::bytes_channel().unwrap();
if let Err(e) = self.0.send(CompositorMsg::SendDisplayList {
webview_id,
display_list_descriptor,
display_list_receiver,
}) {
warn!("Error sending display list: {}", e);
}
let display_list_info_serialized =
bincode::serialize(&display_list_info).unwrap_or_default();
if let Err(error) = display_list_sender.send(&display_list_info_serialized) {
warn!("Error sending display list info: {error}");
}
if let Err(error) = display_list_sender.send(&display_list_data.items_data) {
warn!("Error sending display list items: {error}");
}
if let Err(error) = display_list_sender.send(&display_list_data.cache_data) {
warn!("Error sending display list cache data: {error}");
}
if let Err(error) = display_list_sender.send(&display_list_data.spatial_tree) {
warn!("Error sending display spatial tree: {error}");
}
}
pub fn hit_test(
&self,
pipeline: Option<WebRenderPipelineId>,
point: DevicePoint,
flags: HitTestFlags,
) -> Vec<CompositorHitTestResult> {
let (sender, receiver) = ipc::channel().unwrap();
self.0
.send(CompositorMsg::HitTest(pipeline, point, flags, sender))
.expect("error sending hit test");
receiver.recv().expect("error receiving hit test result")
}
pub fn generate_image_key(&self) -> Option<ImageKey> {
let (sender, receiver) = ipc::channel().unwrap();
self.0.send(CompositorMsg::GenerateImageKey(sender)).ok()?;
receiver.recv().ok()
}
pub fn add_image(
&self,
key: ImageKey,
descriptor: ImageDescriptor,
data: SerializableImageData,
) {
if let Err(e) = self.0.send(CompositorMsg::AddImage(key, descriptor, data)) {
warn!("Error sending image update: {}", e);
}
}
pub fn update_images(&self, updates: Vec<ImageUpdate>) {
if let Err(e) = self.0.send(CompositorMsg::UpdateImages(updates)) {
warn!("error sending image updates: {}", e);
}
}
pub fn remove_unused_font_resources(
&self,
keys: Vec<FontKey>,
instance_keys: Vec<FontInstanceKey>,
) {
if keys.is_empty() && instance_keys.is_empty() {
return;
}
let _ = self.0.send(CompositorMsg::RemoveFonts(keys, instance_keys));
}
pub fn add_font_instance(
&self,
font_instance_key: FontInstanceKey,
font_key: FontKey,
size: f32,
flags: FontInstanceFlags,
) {
let _x = self.0.send(CompositorMsg::AddFontInstance(
font_instance_key,
font_key,
size,
flags,
));
}
pub fn add_font(&self, font_key: FontKey, data: Arc<IpcSharedMemory>, index: u32) {
let _ = self.0.send(CompositorMsg::AddFont(font_key, data, index));
}
pub fn add_system_font(&self, font_key: FontKey, handle: NativeFontHandle) {
let _ = self.0.send(CompositorMsg::AddSystemFont(font_key, handle));
}
pub fn fetch_font_keys(
&self,
number_of_font_keys: usize,
number_of_font_instance_keys: usize,
) -> (Vec<FontKey>, Vec<FontInstanceKey>) {
let (sender, receiver) = ipc_channel::ipc::channel().expect("Could not create IPC channel");
let _ = self.0.send(CompositorMsg::GenerateFontKeys(
number_of_font_keys,
number_of_font_instance_keys,
sender,
));
receiver.recv().unwrap()
}
}
pub trait WebrenderExternalImageApi {
fn lock(&mut self, id: u64) -> (WebrenderImageSource, UntypedSize2D<i32>);
fn unlock(&mut self, id: u64);
}
pub enum WebrenderImageSource<'a> {
TextureHandle(u32),
Raw(&'a [u8]),
}
pub enum WebrenderImageHandlerType {
WebGL,
Media,
WebGPU,
}
#[derive(Default)]
pub struct WebrenderExternalImageRegistry {
external_images: HashMap<ExternalImageId, WebrenderImageHandlerType>,
next_image_id: u64,
}
impl WebrenderExternalImageRegistry {
pub fn next_id(&mut self, handler_type: WebrenderImageHandlerType) -> ExternalImageId {
self.next_image_id += 1;
let key = ExternalImageId(self.next_image_id);
self.external_images.insert(key, handler_type);
key
}
pub fn remove(&mut self, key: &ExternalImageId) {
self.external_images.remove(key);
}
pub fn get(&self, key: &ExternalImageId) -> Option<&WebrenderImageHandlerType> {
self.external_images.get(key)
}
}
pub struct WebrenderExternalImageHandlers {
webgl_handler: Option<Box<dyn WebrenderExternalImageApi>>,
media_handler: Option<Box<dyn WebrenderExternalImageApi>>,
webgpu_handler: Option<Box<dyn WebrenderExternalImageApi>>,
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
}
impl WebrenderExternalImageHandlers {
pub fn new() -> (Self, Arc<Mutex<WebrenderExternalImageRegistry>>) {
let external_images = Arc::new(Mutex::new(WebrenderExternalImageRegistry::default()));
(
Self {
webgl_handler: None,
media_handler: None,
webgpu_handler: None,
external_images: external_images.clone(),
},
external_images,
)
}
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) -> ExternalImage {
let external_images = self.external_images.lock().unwrap();
let handler_type = external_images
.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 {
WebrenderImageSource::TextureHandle(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 {
WebrenderImageSource::TextureHandle(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);
let buffer = match source {
WebrenderImageSource::Raw(b) => b,
_ => panic!("Wrong type"),
};
ExternalImage {
uv: TexelRect::new(0.0, size.height as f32, size.width as f32, 0.0),
source: ExternalImageSource::RawData(buffer),
}
},
}
}
fn unlock(&mut self, key: ExternalImageId, _channel_index: u8) {
let external_images = self.external_images.lock().unwrap();
let handler_type = external_images
.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),
DeleteImage(ImageKey),
UpdateImage(ImageKey, ImageDescriptor, SerializableImageData),
}
#[derive(Debug, Deserialize, Serialize)]
pub enum SerializableImageData {
Raw(IpcSharedMemory),
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);
}