Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 13 additions & 55 deletions components/compositing/compositor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,10 @@ use dpi::PhysicalSize;
use embedder_traits::{
CompositorHitTestResult, InputEvent, ScreenshotCaptureError, ShutdownState, ViewportDetails,
};
use euclid::{Point2D, Rect, Scale, Size2D, Transform3D};
use euclid::{Point2D, Scale, Size2D, Transform3D};
use image::RgbaImage;
use ipc_channel::ipc::{self, IpcSharedMemory};
use log::{debug, info, trace, warn};
use pixels::{CorsStatus, ImageFrame, ImageMetadata, PixelFormat, RasterImage};
use profile_traits::mem::{
ProcessReports, ProfilerRegistration, Report, ReportKind, perform_memory_report,
};
Expand Down Expand Up @@ -1184,58 +1183,6 @@ impl IOCompositor {
self.needs_repaint.set(RepaintReason::empty());
}

/// Render the WebRender scene to the shared memory, without updating other state of this
/// [`IOCompositor`]. If succesful return the output image in shared memory.
pub fn render_to_shared_memory(
&mut self,
webview_id: WebViewId,
page_rect: Option<Rect<f32, CSSPixel>>,
) -> Option<RasterImage> {
self.render_inner();

let size = self.rendering_context.size2d().to_i32();
let rect = if let Some(rect) = page_rect {
let scale = self
.webview_renderers
.get(webview_id)
.map(WebViewRenderer::device_pixels_per_page_pixel)
.unwrap_or_else(|| Scale::new(1.0));
let rect = scale.transform_rect(&rect);

let x = rect.origin.x as i32;
// We need to convert to the bottom-left origin coordinate
// system used by OpenGL
// If dpi > 1, y can be computed to be -1 due to rounding issue, resulting in panic.
// https://github.com/servo/servo/issues/39306#issuecomment-3342204869
let y = 0.max((size.height as f32 - rect.origin.y - rect.size.height) as i32);
let w = rect.size.width as i32;
let h = rect.size.height as i32;

DeviceIntRect::from_origin_and_size(Point2D::new(x, y), Size2D::new(w, h))
} else {
DeviceIntRect::from_origin_and_size(Point2D::origin(), size)
};

self.rendering_context
.read_to_image(rect)
.map(|image| RasterImage {
metadata: ImageMetadata {
width: image.width(),
height: image.height(),
},
format: PixelFormat::RGBA8,
frames: vec![ImageFrame {
delay: None,
byte_range: 0..image.len(),
width: image.width(),
height: image.height(),
}],
bytes: ipc::IpcSharedMemory::from_bytes(&image),
id: None,
cors_status: CorsStatus::Safe,
})
}

#[servo_tracing::instrument(skip_all)]
fn render_inner(&mut self) {
if let Err(err) = self.rendering_context.make_current() {
Expand Down Expand Up @@ -1584,6 +1531,16 @@ impl IOCompositor {
}
}

pub fn device_pixels_per_page_pixel(
&self,
webview_id: WebViewId,
) -> Scale<f32, CSSPixel, DevicePixel> {
self.webview_renderers
.get(webview_id)
.map(WebViewRenderer::device_pixels_per_page_pixel)
.unwrap_or_default()
}

fn webrender_document(&self) -> DocumentId {
self.global.borrow().webrender_document
}
Expand Down Expand Up @@ -1644,10 +1601,11 @@ impl IOCompositor {
pub fn request_screenshot(
&self,
webview_id: WebViewId,
rect: Option<DeviceRect>,
callback: Box<dyn FnOnce(Result<RgbaImage, ScreenshotCaptureError>) + 'static>,
) {
self.screenshot_taker
.request_screenshot(webview_id, callback);
.request_screenshot(webview_id, rect, callback);
let _ = self.global.borrow().constellation_sender.send(
EmbedderToConstellationMessage::RequestScreenshotReadiness(webview_id),
);
Expand Down
27 changes: 24 additions & 3 deletions components/compositing/screenshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,19 @@ use std::rc::Rc;
use base::Epoch;
use base::id::{PipelineId, WebViewId};
use embedder_traits::ScreenshotCaptureError;
use euclid::{Point2D, Size2D};
use image::RgbaImage;
use rustc_hash::FxHashMap;
use webrender_api::units::{DeviceIntRect, DeviceRect};

use crate::IOCompositor;
use crate::compositor::RepaintReason;

pub(crate) struct ScreenshotRequest {
webview_id: WebViewId,
phase: ScreenshotRequestPhase,
rect: Option<DeviceRect>,
callback: Box<dyn FnOnce(Result<RgbaImage, ScreenshotCaptureError>) + 'static>,
phase: ScreenshotRequestPhase,
}

/// Screenshots requests happen in three phases:
Expand Down Expand Up @@ -63,12 +66,14 @@ impl ScreenshotTaker {
pub(crate) fn request_screenshot(
&self,
webview_id: WebViewId,
rect: Option<DeviceRect>,
callback: Box<dyn FnOnce(Result<RgbaImage, ScreenshotCaptureError>) + 'static>,
) {
self.requests.borrow_mut().push(ScreenshotRequest {
webview_id,
phase: ScreenshotRequestPhase::ConstellationRequest,
rect,
callback,
phase: ScreenshotRequestPhase::ConstellationRequest,
});
}

Expand Down Expand Up @@ -175,9 +180,25 @@ impl ScreenshotTaker {
return None;
};

let viewport_rect = webview_renderer.rect.to_i32();
let viewport_size = viewport_rect.size();
let rect = screenshot_request.rect.map_or(viewport_rect, |rect| {
// We need to convert to the bottom-left origin coordinate
// system used by OpenGL
// If dpi > 1, y can be computed to be -1 due to rounding issue, resulting in panic.
// https://github.com/servo/servo/issues/39306#issuecomment-3342204869
let x = rect.min.x as i32;
let y = 0.max(
(viewport_size.height as f32 - rect.min.y - rect.size().height) as i32,
);
let w = rect.size().width as i32;
let h = rect.size().height as i32;

DeviceIntRect::from_origin_and_size(Point2D::new(x, y), Size2D::new(w, h))
});
let result = renderer
.rendering_context()
.read_to_image(webview_renderer.rect.to_i32())
.read_to_image(rect)
.ok_or(ScreenshotCaptureError::CouldNotReadImage);
callback(result);
None
Expand Down
28 changes: 4 additions & 24 deletions components/servo/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,14 @@ use fonts::SystemFontService;
use gaol::sandbox::{ChildSandbox, ChildSandboxMethods};
pub use gleam::gl;
use gleam::gl::RENDERER;
pub use image::RgbaImage;
use ipc_channel::ipc::{self, IpcSender};
use javascript_evaluator::JavaScriptEvaluator;
pub use keyboard_types::{
Code, CompositionEvent, CompositionState, Key, KeyState, Location, Modifiers, NamedKey,
};
use layout::LayoutFactoryImpl;
use log::{Log, Metadata, Record, debug, error, warn};
use log::{Log, Metadata, Record, debug, warn};
use media::{GlApi, NativeDisplay, WindowGLContext};
use net::protocols::ProtocolRegistry;
use net::resource_thread::new_resource_threads;
Expand Down Expand Up @@ -1059,29 +1060,8 @@ impl Servo {
}

pub fn execute_webdriver_command(&self, command: WebDriverCommandMsg) {
if let WebDriverCommandMsg::TakeScreenshot(webview_id, page_rect, response_sender) = command
{
if let Some(ref rect) = page_rect {
if rect.height() == 0.0 || rect.width() == 0.0 {
error!("Taking screenshot of bounding box with zero area");
if let Err(e) = response_sender.send(Err(())) {
error!("Sending reply to create png failed {e:?}");
}
return;
}
}

let img = self
.compositor
.borrow_mut()
.render_to_shared_memory(webview_id, page_rect);
if let Err(e) = response_sender.send(Ok(img)) {
error!("Sending reply to create png failed ({:?}).", e);
}
} else {
self.constellation_proxy
.send(EmbedderToConstellationMessage::WebDriverCommand(command));
}
self.constellation_proxy
.send(EmbedderToConstellationMessage::WebDriverCommand(command));
}

pub fn set_preference(&self, name: &str, value: PrefValue) {
Expand Down
19 changes: 15 additions & 4 deletions components/servo/webview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use embedder_traits::{
use euclid::{Point2D, Scale, Size2D};
use image::RgbaImage;
use servo_geometry::DeviceIndependentPixel;
use style_traits::CSSPixel;
use url::Url;
use webrender_api::ScrollLocation;
use webrender_api::units::{DeviceIntPoint, DevicePixel, DeviceRect};
Expand Down Expand Up @@ -526,6 +527,13 @@ impl WebView {
.set_pinch_zoom(self.id(), new_pinch_zoom);
}

pub fn device_pixels_per_css_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> {
self.inner()
.compositor
.borrow()
.device_pixels_per_page_pixel(self.id())
}

pub fn exit_fullscreen(&self) {
self.inner()
.constellation_proxy
Expand Down Expand Up @@ -589,9 +597,11 @@ impl WebView {
);
}

/// Asynchronously take a screenshot of the [`WebView`] contents. This method will
/// wait until the [`WebView`] is ready before the screenshot is taken. This includes
/// waiting for:
/// Asynchronously take a screenshot of the [`WebView`] contents, given a `rect` or the whole
/// viewport, if no `rect` is given.
///
/// This method will wait until the [`WebView`] is ready before the screenshot is taken.
/// This includes waiting for:
///
/// - all frames to fire their `load` event.
/// - all render blocking elements, such as stylesheets included via the `<link>`
Expand All @@ -606,12 +616,13 @@ impl WebView {
/// operation.
pub fn take_screenshot(
&self,
rect: Option<DeviceRect>,
callback: impl FnOnce(Result<RgbaImage, ScreenshotCaptureError>) + 'static,
) {
self.inner()
.compositor
.borrow()
.request_screenshot(self.id(), Box::new(callback));
.request_screenshot(self.id(), rect, Box::new(callback));
}
}

Expand Down
1 change: 1 addition & 0 deletions components/shared/embedder/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ crossbeam-channel = { workspace = true }
euclid = { workspace = true }
http = { workspace = true }
hyper_serde = { workspace = true }
image = { workspace = true }
ipc-channel = { workspace = true }
keyboard-types = { workspace = true }
log = { workspace = true }
Expand Down
9 changes: 5 additions & 4 deletions components/shared/embedder/webdriver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ use std::collections::HashMap;
use base::generic_channel::GenericSender;
use base::id::{BrowsingContextId, WebViewId};
use cookie::Cookie;
use crossbeam_channel::Sender;
use euclid::default::Rect as UntypedRect;
use euclid::{Rect, Size2D};
use hyper_serde::Serde;
use image::RgbaImage;
use ipc_channel::ipc::IpcSender;
use keyboard_types::{CompositionEvent, KeyboardEvent};
use pixels::RasterImage;
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};
use servo_geometry::DeviceIndependentIntRect;
Expand All @@ -23,7 +24,7 @@ use style_traits::CSSPixel;
use webdriver::error::ErrorStatus;
use webrender_api::units::DevicePixel;

use crate::{JSValue, MouseButton, MouseButtonAction, TraversalId};
use crate::{JSValue, MouseButton, MouseButtonAction, ScreenshotCaptureError, TraversalId};

#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct WebDriverMessageId(pub usize);
Expand Down Expand Up @@ -73,7 +74,7 @@ impl WebDriverUserPromptAction {
}

/// Messages to the constellation originating from the WebDriver server.
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug)]
pub enum WebDriverCommandMsg {
/// Used in the initialization of the WebDriver server to set the sender for sending responses
/// back to the WebDriver client. It is set to constellation for now
Expand Down Expand Up @@ -144,7 +145,7 @@ pub enum WebDriverCommandMsg {
TakeScreenshot(
WebViewId,
Option<Rect<f32, CSSPixel>>,
IpcSender<Result<Option<RasterImage>, ()>>,
Sender<Result<RgbaImage, ScreenshotCaptureError>>,
),
/// Create a new webview that loads about:blank. The embedder will use
/// the provided channels to return the top level browsing context id
Expand Down
Loading
Loading