diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 90f5b1768f0..217db15254d 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -7,6 +7,7 @@ use std::collections::HashMap; use std::env; use std::fs::{create_dir_all, File}; use std::io::Write; +use std::iter::once; use std::num::NonZeroU32; use std::rc::Rc; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; @@ -27,7 +28,7 @@ use ipc_channel::ipc; use libc::c_void; use log::{debug, error, info, trace, warn}; use msg::constellation_msg::{ - PipelineId, PipelineIndex, PipelineNamespaceId, TopLevelBrowsingContextId, + PipelineId, PipelineIndex, PipelineNamespaceId, TopLevelBrowsingContextId, WebViewId, }; use net_traits::image::base::Image; use net_traits::image_cache::CorsStatus; @@ -44,7 +45,7 @@ use servo_geometry::{DeviceIndependentPixel, FramebufferUintLength}; use style_traits::{CSSPixel, DevicePixel, PinchZoomFactor}; use webrender::{CaptureBits, RenderApi, Transaction}; use webrender_api::units::{ - DeviceIntPoint, DeviceIntSize, DevicePoint, LayoutPoint, LayoutRect, LayoutSize, + DeviceIntPoint, DeviceIntSize, DevicePoint, DeviceRect, LayoutPoint, LayoutRect, LayoutSize, LayoutVector2D, WorldPoint, }; use webrender_api::{ @@ -56,6 +57,7 @@ use webrender_api::{ use crate::gl::RenderTargetInfo; use crate::touch::{TouchAction, TouchHandler}; +use crate::webview::{UnknownWebView, WebView, WebViewAlreadyExists, WebViewManager}; use crate::windowing::{ self, EmbedderCoordinates, MouseWindowEvent, WebRenderDebugOption, WindowMethods, }; @@ -109,11 +111,6 @@ impl FrameTreeId { } } -struct RootPipeline { - top_level_browsing_context_id: TopLevelBrowsingContextId, - id: Option, -} - /// NB: Never block on the constellation, because sometimes the constellation blocks on us. pub struct IOCompositor { /// The application window. @@ -122,10 +119,8 @@ pub struct IOCompositor { /// The port on which we receive messages. port: CompositorReceiver, - /// The root content pipeline ie the pipeline which contains the main frame - /// to display. In the WebRender scene, this will be the only child of another - /// pipeline which applies a pinch zoom transformation. - root_content_pipeline: RootPipeline, + /// Our top-level browsing contexts. + webviews: WebViewManager, /// Tracks details about each active pipeline that the compositor knows about. pipeline_details: HashMap, @@ -292,6 +287,9 @@ struct PipelineDetails { /// The pipeline associated with this PipelineDetails object. pipeline: Option, + /// The id of the parent pipeline, if any. + parent_pipeline_id: Option, + /// The epoch of the most recent display list for this pipeline. Note that this display /// list might not be displayed, as WebRender processes display lists asynchronously. most_recent_display_list_epoch: Option, @@ -318,6 +316,7 @@ impl PipelineDetails { fn new() -> PipelineDetails { PipelineDetails { pipeline: None, + parent_pipeline_id: None, most_recent_display_list_epoch: None, animations_running: false, animation_callbacks_running: false, @@ -376,14 +375,26 @@ impl IOCompositor { convert_mouse_to_touch: bool, top_level_browsing_context_id: TopLevelBrowsingContextId, ) -> Self { + let embedder_coordinates = window.get_coordinates(); + let mut webviews = WebViewManager::default(); + webviews + .add( + top_level_browsing_context_id, + WebView { + pipeline_id: None, + rect: embedder_coordinates.get_viewport().to_f32(), + }, + ) + .expect("Infallible with a new WebViewManager"); + webviews + .show(top_level_browsing_context_id) + .expect("Infallible due to add"); + IOCompositor { embedder_coordinates: window.get_coordinates(), window, port: state.receiver, - root_content_pipeline: RootPipeline { - top_level_browsing_context_id, - id: None, - }, + webviews, pipeline_details: HashMap::new(), composition_request: CompositionRequest::NoCompositingNecessary, touch_handler: TouchHandler::new(), @@ -524,6 +535,8 @@ impl IOCompositor { } fn handle_browser_message(&mut self, msg: CompositorMsg) -> bool { + trace_msg_from_constellation!(msg, "{msg:?}"); + match self.shutdown_state { ShutdownState::NotShuttingDown => {}, ShutdownState::ShuttingDown => { @@ -546,11 +559,40 @@ impl IOCompositor { self.change_running_animations_state(pipeline_id, animation_state); }, - CompositorMsg::SetFrameTree(frame_tree) => { - self.set_frame_tree(&frame_tree); + CompositorMsg::CreateOrUpdateWebView(frame_tree) => { + self.set_frame_tree_for_webview(&frame_tree); self.send_scroll_positions_to_layout_for_pipeline(&frame_tree.pipeline.id); }, + CompositorMsg::RemoveWebView(top_level_browsing_context_id) => { + self.remove_webview(top_level_browsing_context_id); + }, + + CompositorMsg::MoveResizeWebView(webview_id, rect) => { + self.move_resize_webview(webview_id, rect); + }, + + CompositorMsg::ShowWebView(webview_id, hide_others) => { + if let Err(UnknownWebView(webview_id)) = self.show_webview(webview_id, hide_others) + { + warn!("{webview_id}: ShowWebView on unknown webview id"); + } + }, + + CompositorMsg::HideWebView(webview_id) => { + if let Err(UnknownWebView(webview_id)) = self.hide_webview(webview_id) { + warn!("{webview_id}: HideWebView on unknown webview id"); + } + }, + + CompositorMsg::RaiseWebViewToTop(webview_id, hide_others) => { + if let Err(UnknownWebView(webview_id)) = + self.raise_webview_to_top(webview_id, hide_others) + { + warn!("{webview_id}: RaiseWebViewToTop on unknown webview id"); + } + }, + CompositorMsg::TouchEventProcessed(result) => { self.touch_handler.on_event_processed(result); }, @@ -1016,16 +1058,21 @@ impl IOCompositor { } } - /// Set the root pipeline for our WebRender scene. If there is no pinch zoom applied, - /// the root pipeline is the root content pipeline. If there is pinch zoom, the root - /// content pipeline is wrapped in a display list that applies a pinch zoom - /// transformation to it. - fn set_root_content_pipeline_handling_device_scaling(&self, transaction: &mut Transaction) { - let root_content_pipeline = match self.root_content_pipeline.id { - Some(id) => id.to_webrender(), - None => return, - }; + /// Set the root pipeline for our WebRender scene to a display list that consists of an iframe + /// for each visible top-level browsing context, applying a transformation on the root for + /// pinch zoom, page zoom, and HiDPI scaling. + fn send_root_pipeline_display_list(&mut self) { + let mut transaction = Transaction::new(); + self.send_root_pipeline_display_list_in_transaction(&mut transaction); + self.generate_frame(&mut transaction, RenderReasons::SCENE); + self.webrender_api + .send_transaction(self.webrender_document, transaction); + } + /// Set the root pipeline for our WebRender scene to a display list that consists of an iframe + /// for each visible top-level browsing context, applying a transformation on the root for + /// pinch zoom, page zoom, and HiDPI scaling. + fn send_root_pipeline_display_list_in_transaction(&self, transaction: &mut Transaction) { // Every display list needs a pipeline, but we'd like to choose one that is unlikely // to conflict with our content pipelines, which start at (1, 1). (0, 0) is WebRender's // dummy pipeline, so we choose (0, 1). @@ -1034,10 +1081,6 @@ impl IOCompositor { let mut builder = webrender_api::DisplayListBuilder::new(root_pipeline); builder.begin(); - let viewport_size = LayoutSize::new( - self.embedder_coordinates.get_viewport().width() as f32, - self.embedder_coordinates.get_viewport().height() as f32, - ); let zoom_factor = self.device_pixels_per_page_pixel().0; let zoom_reference_frame = builder.push_reference_frame( @@ -1053,26 +1096,30 @@ impl IOCompositor { SpatialTreeItemKey::new(0, 0), ); - let scaled_viewport_rect = LayoutRect::from_origin_and_size( - LayoutPoint::zero(), - LayoutSize::new( - viewport_size.width / zoom_factor, - viewport_size.height / zoom_factor, - ), - ); + let scaled_viewport_size = + self.embedder_coordinates.get_viewport().size().to_f32() / zoom_factor; + let scaled_viewport_size = LayoutSize::from_untyped(scaled_viewport_size.to_untyped()); + let scaled_viewport_rect = + LayoutRect::from_origin_and_size(LayoutPoint::zero(), scaled_viewport_size); let root_clip_id = builder.define_clip_rect(zoom_reference_frame, scaled_viewport_rect); let clip_chain_id = builder.define_clip_chain(None, [root_clip_id]); - builder.push_iframe( - scaled_viewport_rect, - scaled_viewport_rect, - &SpaceAndClipInfo { - spatial_id: zoom_reference_frame, - clip_chain_id, - }, - root_content_pipeline, - true, - ); + for (_, webview) in self.webviews.painting_order() { + if let Some(pipeline_id) = webview.pipeline_id { + let scaled_webview_rect = webview.rect / zoom_factor; + builder.push_iframe( + LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()), + LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()), + &SpaceAndClipInfo { + spatial_id: zoom_reference_frame, + clip_chain_id, + }, + pipeline_id.to_webrender(), + true, + ); + } + } + let built_display_list = builder.end(); // NB: We are always passing 0 as the epoch here, but this doesn't seem to @@ -1109,29 +1156,163 @@ impl IOCompositor { } } - fn set_frame_tree(&mut self, frame_tree: &SendableFrameTree) { - debug!( - "Setting the frame tree for pipeline {:?}", - frame_tree.pipeline.id - ); + fn set_frame_tree_for_webview(&mut self, frame_tree: &SendableFrameTree) { + debug!("{}: Setting frame tree for webview", frame_tree.pipeline.id); - self.root_content_pipeline = RootPipeline { - top_level_browsing_context_id: frame_tree.pipeline.top_level_browsing_context_id, - id: Some(frame_tree.pipeline.id), - }; + let top_level_browsing_context_id = frame_tree.pipeline.top_level_browsing_context_id; + if let Some(webview) = self.webviews.get_mut(top_level_browsing_context_id) { + let new_pipeline_id = Some(frame_tree.pipeline.id); + if new_pipeline_id != webview.pipeline_id { + debug!( + "{:?}: Updating webview from pipeline {:?} to {:?}", + top_level_browsing_context_id, webview.pipeline_id, new_pipeline_id + ); + } + webview.pipeline_id = new_pipeline_id; + } else { + let top_level_browsing_context_id = frame_tree.pipeline.top_level_browsing_context_id; + let pipeline_id = Some(frame_tree.pipeline.id); + debug!( + "{:?}: Creating new webview with pipeline {:?}", + top_level_browsing_context_id, pipeline_id + ); + if let Err(WebViewAlreadyExists(webview_id)) = self.webviews.add( + top_level_browsing_context_id, + WebView { + pipeline_id, + rect: self.embedder_coordinates.get_viewport().to_f32(), + }, + ) { + error!("{webview_id}: Creating webview that already exists"); + return; + } + } - let mut txn = Transaction::new(); - self.set_root_content_pipeline_handling_device_scaling(&mut txn); - self.generate_frame(&mut txn, RenderReasons::SCENE); - self.webrender_api - .send_transaction(self.webrender_document, txn); - - self.create_pipeline_details_for_frame_tree(frame_tree); - self.reset_scroll_tree_for_unattached_pipelines(frame_tree); + self.send_root_pipeline_display_list(); + self.create_or_update_pipeline_details_with_frame_tree(&frame_tree, None); + self.reset_scroll_tree_for_unattached_pipelines(&frame_tree); self.frame_tree_id.next(); } + fn remove_webview(&mut self, top_level_browsing_context_id: TopLevelBrowsingContextId) { + debug!("{}: Removing", top_level_browsing_context_id); + let Ok(webview) = self.webviews.remove(top_level_browsing_context_id) else { + warn!("{top_level_browsing_context_id}: Removing unknown webview"); + return; + }; + + self.send_root_pipeline_display_list(); + if let Some(pipeline_id) = webview.pipeline_id { + self.remove_pipeline_details_recursively(pipeline_id); + } + + self.frame_tree_id.next(); + } + + pub fn move_resize_webview(&mut self, webview_id: TopLevelBrowsingContextId, rect: DeviceRect) { + debug!("{webview_id}: Moving and/or resizing webview; rect={rect:?}"); + let rect_changed; + let size_changed; + match self.webviews.get_mut(webview_id) { + Some(webview) => { + rect_changed = rect != webview.rect; + size_changed = rect.size() != webview.rect.size(); + webview.rect = rect; + }, + None => { + warn!("{webview_id}: MoveResizeWebView on unknown webview id"); + return; + }, + }; + + if rect_changed { + if size_changed { + self.send_window_size_message_for_top_level_browser_context(rect, webview_id); + } + + self.send_root_pipeline_display_list(); + } + } + + pub fn show_webview( + &mut self, + webview_id: WebViewId, + hide_others: bool, + ) -> Result<(), UnknownWebView> { + debug!("{webview_id}: Showing webview; hide_others={hide_others}"); + let painting_order_changed = if hide_others { + let result = self + .webviews + .painting_order() + .map(|(&id, _)| id) + .ne(once(webview_id)); + self.webviews.hide_all(); + self.webviews.show(webview_id)?; + result + } else { + self.webviews.show(webview_id)? + }; + if painting_order_changed { + self.send_root_pipeline_display_list(); + } + Ok(()) + } + + pub fn hide_webview(&mut self, webview_id: WebViewId) -> Result<(), UnknownWebView> { + debug!("{webview_id}: Hiding webview"); + if self.webviews.hide(webview_id)? { + self.send_root_pipeline_display_list(); + } + Ok(()) + } + + pub fn raise_webview_to_top( + &mut self, + webview_id: WebViewId, + hide_others: bool, + ) -> Result<(), UnknownWebView> { + debug!("{webview_id}: Raising webview to top; hide_others={hide_others}"); + let painting_order_changed = if hide_others { + let result = self + .webviews + .painting_order() + .map(|(&id, _)| id) + .ne(once(webview_id)); + self.webviews.hide_all(); + self.webviews.raise_to_top(webview_id)?; + result + } else { + self.webviews.raise_to_top(webview_id)? + }; + if painting_order_changed { + self.send_root_pipeline_display_list(); + } + Ok(()) + } + + fn send_window_size_message_for_top_level_browser_context( + &self, + rect: DeviceRect, + top_level_browsing_context_id: TopLevelBrowsingContextId, + ) { + // The device pixel ratio used by the style system should include the scale from page pixels + // to device pixels, but not including any pinch zoom. + let device_pixel_ratio = self.device_pixels_per_page_pixel_not_including_page_zoom(); + let initial_viewport = rect.size().to_f32() / device_pixel_ratio; + let msg = ConstellationMsg::WindowSize( + top_level_browsing_context_id, + WindowSizeData { + device_pixel_ratio, + initial_viewport, + }, + WindowSizeType::Resize, + ); + if let Err(e) = self.constellation_chan.send(msg) { + warn!("Sending window resize to constellation failed ({:?}).", e); + } + } + fn reset_scroll_tree_for_unattached_pipelines(&mut self, frame_tree: &SendableFrameTree) { // TODO(mrobinson): Eventually this can selectively preserve the scroll trees // state for some unattached pipelines in order to preserve scroll position when @@ -1159,11 +1340,35 @@ impl IOCompositor { }) } - fn create_pipeline_details_for_frame_tree(&mut self, frame_tree: &SendableFrameTree) { - self.pipeline_details(frame_tree.pipeline.id).pipeline = Some(frame_tree.pipeline.clone()); + fn create_or_update_pipeline_details_with_frame_tree( + &mut self, + frame_tree: &SendableFrameTree, + parent_pipeline_id: Option, + ) { + let pipeline_id = frame_tree.pipeline.id; + let pipeline_details = self.pipeline_details(pipeline_id); + pipeline_details.pipeline = Some(frame_tree.pipeline.clone()); + pipeline_details.parent_pipeline_id = parent_pipeline_id; for kid in &frame_tree.children { - self.create_pipeline_details_for_frame_tree(kid); + self.create_or_update_pipeline_details_with_frame_tree(kid, Some(pipeline_id)); + } + } + + fn remove_pipeline_details_recursively(&mut self, pipeline_id: PipelineId) { + self.pipeline_details.remove(&pipeline_id); + + let children = self + .pipeline_details + .iter() + .filter(|(_, pipeline_details)| { + pipeline_details.parent_pipeline_id == Some(pipeline_id) + }) + .map(|(&pipeline_id, _)| pipeline_id) + .collect::>(); + + for kid in children { + self.remove_pipeline_details_recursively(kid); } } @@ -1225,9 +1430,10 @@ impl IOCompositor { MouseWindowEvent::MouseUp(_, p) => p, }; - let result = match self.hit_test_at_point(point) { - Some(result) => result, - None => return, + let Some(result) = self.hit_test_at_point(point) else { + // TODO: Notify embedder that the event failed to hit test to any webview. + // TODO: Also notify embedder if an event hits a webview but isn’t consumed? + return; }; let (button, event_type) = match mouse_window_event { @@ -1264,13 +1470,6 @@ impl IOCompositor { flags: HitTestFlags, pipeline_id: Option, ) -> Vec { - let root_pipeline_id = match self.root_content_pipeline.id { - Some(root_pipeline_id) => root_pipeline_id, - None => return vec![], - }; - if self.pipeline(root_pipeline_id).is_none() { - return vec![]; - } // DevicePoint and WorldPoint are the same for us. let world_point = WorldPoint::from_untyped(point.to_untyped()); let results = @@ -1526,7 +1725,7 @@ impl IOCompositor { let mut transaction = Transaction::new(); if zoom_changed { - self.set_root_content_pipeline_handling_device_scaling(&mut transaction); + self.send_root_pipeline_display_list_in_transaction(&mut transaction); } if let Some((pipeline_id, external_id, offset)) = scroll_result { @@ -1670,33 +1869,15 @@ impl IOCompositor { } fn update_after_zoom_or_hidpi_change(&mut self) { - // The device pixel ratio used by the style system should include the scale from page pixels - // to device pixels, but not including any pinch zoom. - let device_pixel_ratio = self.device_pixels_per_page_pixel_not_including_page_zoom(); - let initial_viewport = - self.embedder_coordinates.viewport.size().to_f32() / device_pixel_ratio; - let data = WindowSizeData { - device_pixel_ratio, - initial_viewport, - }; - - let top_level_browsing_context_id = - self.root_content_pipeline.top_level_browsing_context_id; - - let msg = ConstellationMsg::WindowSize( - top_level_browsing_context_id, - data, - WindowSizeType::Resize, - ); - if let Err(e) = self.constellation_chan.send(msg) { - warn!("Sending window resize to constellation failed ({:?}).", e); + for (top_level_browsing_context_id, webview) in self.webviews.painting_order() { + self.send_window_size_message_for_top_level_browser_context( + webview.rect, + *top_level_browsing_context_id, + ); } // Update the root transform in WebRender to reflect the new zoom. - let mut transaction = webrender::Transaction::new(); - self.set_root_content_pipeline_handling_device_scaling(&mut transaction); - self.webrender_api - .send_transaction(self.webrender_document, transaction); + self.send_root_pipeline_display_list(); } /// Simulate a pinch zoom @@ -1928,6 +2109,10 @@ impl IOCompositor { // and check if it is the one layout is expecting, let epoch = Epoch(epoch); if *pending_epoch != epoch { + warn!( + "{}: paint metrics: pending {:?} should be {:?}", + id, pending_epoch, epoch + ); continue; } // in which case, we remove it from the list of pending metrics, @@ -2032,12 +2217,11 @@ impl IOCompositor { }, }; - // Nottify embedder that servo is ready to present. + // Notify embedder that servo is ready to present. // Embedder should call `present` to tell compositor to continue rendering. self.waiting_on_present = true; - let msg = ConstellationMsg::ReadyToPresent( - self.root_content_pipeline.top_level_browsing_context_id, - ); + let webview_ids = self.webviews.painting_order().map(|(&id, _)| id); + let msg = ConstellationMsg::ReadyToPresent(webview_ids.collect()); if let Err(e) = self.constellation_chan.send(msg) { warn!("Sending event to constellation failed ({:?}).", e); } @@ -2077,14 +2261,6 @@ impl IOCompositor { self.assert_gl_framebuffer_complete(); // Set the viewport background based on prefs. - let viewport = self.embedder_coordinates.get_flipped_viewport(); - gl.scissor( - viewport.min.x, - viewport.min.y, - viewport.size().width, - viewport.size().height, - ); - let color = servo_config::pref!(shell.background_color.rgba); gl.clear_color( color[0] as f32, @@ -2092,9 +2268,21 @@ impl IOCompositor { color[2] as f32, color[3] as f32, ); - gl.enable(gleam::gl::SCISSOR_TEST); - gl.clear(gleam::gl::COLOR_BUFFER_BIT); - gl.disable(gleam::gl::SCISSOR_TEST); + + // Clear the viewport rect of each top-level browsing context. + for (_, webview) in self.webviews.painting_order() { + let rect = self.embedder_coordinates.flip_rect(&webview.rect.to_i32()); + gl.scissor( + rect.min.x, + rect.min.y, + rect.size().width, + rect.size().height, + ); + gl.enable(gleam::gl::SCISSOR_TEST); + gl.clear(gleam::gl::COLOR_BUFFER_BIT); + gl.disable(gleam::gl::SCISSOR_TEST); + } + self.assert_gl_framebuffer_complete(); } diff --git a/components/compositing/lib.rs b/components/compositing/lib.rs index a3ae2b48b3b..1be60c17691 100644 --- a/components/compositing/lib.rs +++ b/components/compositing/lib.rs @@ -15,9 +15,13 @@ use webrender_api::DocumentId; pub use crate::compositor::{CompositeTarget, IOCompositor, ShutdownState}; +#[macro_use] +mod tracing; + mod compositor; mod gl; mod touch; +pub mod webview; pub mod windowing; /// Data used to construct a compositor. diff --git a/components/compositing/tracing.rs b/components/compositing/tracing.rs new file mode 100644 index 00000000000..7a53d6f9b84 --- /dev/null +++ b/components/compositing/tracing.rs @@ -0,0 +1,58 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +/// Log an event from constellation at trace level. +/// - To disable tracing: RUST_LOG='compositor { + ::log::trace!(target: $crate::tracing::LogTarget::log_target(&$event), $($rest)+) + }; +} + +/// Get the log target for an event, as a static string. +pub(crate) trait LogTarget { + fn log_target(&self) -> &'static str; +} + +mod from_constellation { + use super::LogTarget; + + macro_rules! target { + ($($name:literal)+) => { + concat!("compositor &'static str { + match self { + Self::ShutdownComplete => target!("ShutdownComplete"), + Self::ChangeRunningAnimationsState(..) => target!("ChangeRunningAnimationsState"), + Self::CreateOrUpdateWebView(..) => target!("CreateOrUpdateWebView"), + Self::RemoveWebView(..) => target!("RemoveWebView"), + Self::MoveResizeWebView(..) => target!("MoveResizeWebView"), + Self::ShowWebView(..) => target!("ShowWebView"), + Self::HideWebView(..) => target!("HideWebView"), + Self::RaiseWebViewToTop(..) => target!("RaiseWebViewToTop"), + Self::TouchEventProcessed(..) => target!("TouchEventProcessed"), + Self::CreatePng(..) => target!("CreatePng"), + Self::IsReadyToSaveImageReply(..) => target!("IsReadyToSaveImageReply"), + Self::SetThrottled(..) => target!("SetThrottled"), + Self::NewWebRenderFrameReady(..) => target!("NewWebRenderFrameReady"), + Self::PipelineExited(..) => target!("PipelineExited"), + Self::PendingPaintMetric(..) => target!("PendingPaintMetric"), + Self::LoadComplete(..) => target!("LoadComplete"), + Self::WebDriverMouseButtonEvent(..) => target!("WebDriverMouseButtonEvent"), + Self::WebDriverMouseMoveEvent(..) => target!("WebDriverMouseMoveEvent"), + Self::GetClientWindow(..) => target!("GetClientWindow"), + Self::GetScreenSize(..) => target!("GetScreenSize"), + Self::GetScreenAvailSize(..) => target!("GetScreenAvailSize"), + Self::Forwarded(..) => target!("Forwarded"), + } + } + } +} diff --git a/components/compositing/webview.rs b/components/compositing/webview.rs new file mode 100644 index 00000000000..b8698381520 --- /dev/null +++ b/components/compositing/webview.rs @@ -0,0 +1,245 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use std::collections::HashMap; + +use msg::constellation_msg::{PipelineId, WebViewId}; +use webrender_api::units::DeviceRect; + +#[derive(Debug, Default)] +pub struct WebView { + pub pipeline_id: Option, + pub rect: DeviceRect, +} + +#[derive(Debug, Default)] +pub struct WebViewManager { + /// Our top-level browsing contexts. In the WebRender scene, their pipelines are the children of + /// a single root pipeline that also applies any pinch zoom transformation. + webviews: HashMap, + + /// The order to paint them in, topmost last. + painting_order: Vec, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct WebViewAlreadyExists(pub WebViewId); + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct UnknownWebView(pub WebViewId); + +impl WebViewManager { + pub fn add( + &mut self, + webview_id: WebViewId, + webview: WebView, + ) -> Result<&mut WebView, WebViewAlreadyExists> { + if self.webviews.contains_key(&webview_id) { + return Err(WebViewAlreadyExists(webview_id)); + } + Ok(self.webviews.entry(webview_id).or_insert(webview)) + } + + pub fn remove(&mut self, webview_id: WebViewId) -> Result { + self.painting_order.retain(|b| *b != webview_id); + self.webviews + .remove(&webview_id) + .ok_or(UnknownWebView(webview_id)) + } + + pub fn get(&self, webview_id: WebViewId) -> Option<&WebView> { + self.webviews.get(&webview_id) + } + + pub fn get_mut(&mut self, webview_id: WebViewId) -> Option<&mut WebView> { + self.webviews.get_mut(&webview_id) + } + + /// Returns true iff the painting order actually changed. + pub fn show(&mut self, webview_id: WebViewId) -> Result { + if !self.webviews.contains_key(&webview_id) { + return Err(UnknownWebView(webview_id)); + } + if !self.painting_order.contains(&webview_id) { + self.painting_order.push(webview_id); + return Ok(true); + } + Ok(false) + } + + /// Returns true iff the painting order actually changed. + pub fn hide(&mut self, webview_id: WebViewId) -> Result { + if !self.webviews.contains_key(&webview_id) { + return Err(UnknownWebView(webview_id)); + } + if self.painting_order.contains(&webview_id) { + self.painting_order.retain(|b| *b != webview_id); + return Ok(true); + } + Ok(false) + } + + /// Returns true iff the painting order actually changed. + pub fn hide_all(&mut self) -> bool { + if !self.painting_order.is_empty() { + self.painting_order.clear(); + return true; + } + false + } + + /// Returns true iff the painting order actually changed. + pub fn raise_to_top(&mut self, webview_id: WebViewId) -> Result { + if !self.webviews.contains_key(&webview_id) { + return Err(UnknownWebView(webview_id)); + } + if self.painting_order.last() != Some(&webview_id) { + self.hide(webview_id)?; + self.show(webview_id)?; + return Ok(true); + } + Ok(false) + } + + pub fn painting_order(&self) -> impl Iterator { + self.painting_order + .iter() + .flat_map(move |webview_id| self.get(*webview_id).map(|b| (webview_id, b))) + } +} + +#[cfg(test)] +mod test { + use std::num::NonZeroU32; + + use msg::constellation_msg::{ + BrowsingContextId, BrowsingContextIndex, PipelineNamespace, PipelineNamespaceId, + TopLevelBrowsingContextId, + }; + + use crate::webview::{UnknownWebView, WebViewAlreadyExists, WebViewManager}; + + fn top_level_id(namespace_id: u32, index: u32) -> TopLevelBrowsingContextId { + TopLevelBrowsingContextId(BrowsingContextId { + namespace_id: PipelineNamespaceId(namespace_id), + index: BrowsingContextIndex(NonZeroU32::new(index).unwrap()), + }) + } + + fn webviews_sorted( + webviews: &WebViewManager, + ) -> Vec<(TopLevelBrowsingContextId, WebView)> { + let mut keys = webviews.webviews.keys().collect::>(); + keys.sort(); + keys.iter() + .map(|&id| (*id, webviews.webviews.get(id).cloned().unwrap())) + .collect() + } + + #[test] + fn test() { + PipelineNamespace::install(PipelineNamespaceId(0)); + let mut webviews = WebViewManager::default(); + + // add() adds the webview to the map, but not the painting order. + assert!(webviews.add(TopLevelBrowsingContextId::new(), 'a').is_ok()); + assert!(webviews.add(TopLevelBrowsingContextId::new(), 'b').is_ok()); + assert!(webviews.add(TopLevelBrowsingContextId::new(), 'c').is_ok()); + assert_eq!( + webviews_sorted(&webviews), + vec![ + (top_level_id(0, 1), 'a'), + (top_level_id(0, 2), 'b'), + (top_level_id(0, 3), 'c'), + ] + ); + assert!(webviews.painting_order.is_empty()); + + // add() returns WebViewAlreadyExists if the webview id already exists. + assert_eq!( + webviews.add(top_level_id(0, 3), 'd'), + Err(WebViewAlreadyExists(top_level_id(0, 3))) + ); + + // Other methods return UnknownWebView or None if the webview id doesn’t exist. + assert_eq!( + webviews.remove(top_level_id(1, 1)), + Err(UnknownWebView(top_level_id(1, 1))) + ); + assert_eq!(webviews.get(top_level_id(1, 1)), None); + assert_eq!(webviews.get_mut(top_level_id(1, 1)), None); + assert_eq!( + webviews.show(top_level_id(1, 1)), + Err(UnknownWebView(top_level_id(1, 1))) + ); + assert_eq!( + webviews.hide(top_level_id(1, 1)), + Err(UnknownWebView(top_level_id(1, 1))) + ); + assert_eq!( + webviews.raise_to_top(top_level_id(1, 1)), + Err(UnknownWebView(top_level_id(1, 1))) + ); + + // For webviews not yet visible, both show() and raise_to_top() add the given webview on top. + assert_eq!(webviews.show(top_level_id(0, 2)), Ok(true)); + assert_eq!(webviews.show(top_level_id(0, 2)), Ok(false)); + assert_eq!(webviews.painting_order, vec![top_level_id(0, 2)]); + assert_eq!(webviews.raise_to_top(top_level_id(0, 1)), Ok(true)); + assert_eq!(webviews.raise_to_top(top_level_id(0, 1)), Ok(false)); + assert_eq!( + webviews.painting_order, + vec![top_level_id(0, 2), top_level_id(0, 1)] + ); + assert_eq!(webviews.show(top_level_id(0, 3)), Ok(true)); + assert_eq!(webviews.show(top_level_id(0, 3)), Ok(false)); + assert_eq!( + webviews.painting_order, + vec![top_level_id(0, 2), top_level_id(0, 1), top_level_id(0, 3)] + ); + + // For webviews already visible, show() does nothing, while raise_to_top() makes it on top. + assert_eq!(webviews.show(top_level_id(0, 1)), Ok(false)); + assert_eq!( + webviews.painting_order, + vec![top_level_id(0, 2), top_level_id(0, 1), top_level_id(0, 3)] + ); + assert_eq!(webviews.raise_to_top(top_level_id(0, 1)), Ok(true)); + assert_eq!(webviews.raise_to_top(top_level_id(0, 1)), Ok(false)); + assert_eq!( + webviews.painting_order, + vec![top_level_id(0, 2), top_level_id(0, 3), top_level_id(0, 1)] + ); + + // hide() removes the webview from the painting order, but not the map. + assert_eq!(webviews.hide(top_level_id(0, 3)), Ok(true)); + assert_eq!(webviews.hide(top_level_id(0, 3)), Ok(false)); + assert_eq!( + webviews.painting_order, + vec![top_level_id(0, 2), top_level_id(0, 1)] + ); + assert_eq!( + webviews_sorted(&webviews), + vec![ + (top_level_id(0, 1), 'a'), + (top_level_id(0, 2), 'b'), + (top_level_id(0, 3), 'c'), + ] + ); + + // painting_order() returns only the visible webviews, in painting order. + let mut painting_order = webviews.painting_order(); + assert_eq!(painting_order.next(), Some((&top_level_id(0, 2), &'b'))); + assert_eq!(painting_order.next(), Some((&top_level_id(0, 1), &'a'))); + assert_eq!(painting_order.next(), None); + drop(painting_order); + + // remove() removes the given webview from both the map and the painting order. + assert!(webviews.remove(top_level_id(0, 1)).is_ok()); + assert!(webviews.remove(top_level_id(0, 2)).is_ok()); + assert!(webviews.remove(top_level_id(0, 3)).is_ok()); + assert!(webviews_sorted(&webviews).is_empty()); + assert!(webviews.painting_order.is_empty()); + } +} diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs index ef3e0450487..924036e54c0 100644 --- a/components/compositing/windowing.rs +++ b/components/compositing/windowing.rs @@ -19,7 +19,7 @@ use script_traits::{ use servo_geometry::DeviceIndependentPixel; use servo_url::ServoUrl; use style_traits::DevicePixel; -use webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePoint}; +use webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePoint, DeviceRect}; use webrender_api::ScrollLocation; #[derive(Clone)] @@ -51,7 +51,7 @@ pub enum EmbedderEvent { /// message, the window must make the same GL context as in `PrepareRenderingEvent` current. Refresh, /// Sent when the window is resized. - Resize, + WindowResize, /// Sent when a navigation request from script is allowed/refused. AllowNavigationResponse(PipelineId, bool), /// Sent when a new URL is to be loaded. @@ -83,15 +83,24 @@ pub enum EmbedderEvent { Keyboard(KeyboardEvent), /// Sent when Ctr+R/Apple+R is called to reload the current page. Reload(TopLevelBrowsingContextId), - /// Create a new top level browsing context + /// Create a new top-level browsing context. NewWebView(ServoUrl, TopLevelBrowsingContextId), - /// Close a top level browsing context + /// Close a top-level browsing context. CloseWebView(TopLevelBrowsingContextId), - /// Panic a top level browsing context. + /// Panic a top-level browsing context. SendError(Option, String), - /// Make a top level browsing context visible, hiding the previous - /// visible one. + /// Move and/or resize a webview to the given rect. + MoveResizeWebView(TopLevelBrowsingContextId, DeviceRect), + /// Start painting a webview, and optionally stop painting all others. + ShowWebView(TopLevelBrowsingContextId, bool), + /// Stop painting a webview. + HideWebView(TopLevelBrowsingContextId), + /// Start painting a webview on top of all others, and optionally stop painting all others. + RaiseWebViewToTop(TopLevelBrowsingContextId, bool), + /// Make a webview focused. FocusWebView(TopLevelBrowsingContextId), + /// Make none of the webviews focused. + BlurWebView, /// Toggles a debug flag in WebRender ToggleWebRenderDebug(WebRenderDebugOption), /// Capture current WebRender @@ -124,7 +133,7 @@ impl Debug for EmbedderEvent { match *self { EmbedderEvent::Idle => write!(f, "Idle"), EmbedderEvent::Refresh => write!(f, "Refresh"), - EmbedderEvent::Resize => write!(f, "Resize"), + EmbedderEvent::WindowResize => write!(f, "Resize"), EmbedderEvent::Keyboard(..) => write!(f, "Keyboard"), EmbedderEvent::AllowNavigationResponse(..) => write!(f, "AllowNavigationResponse"), EmbedderEvent::LoadUrl(..) => write!(f, "LoadUrl"), @@ -139,10 +148,32 @@ impl Debug for EmbedderEvent { EmbedderEvent::Navigation(..) => write!(f, "Navigation"), EmbedderEvent::Quit => write!(f, "Quit"), EmbedderEvent::Reload(..) => write!(f, "Reload"), - EmbedderEvent::NewWebView(..) => write!(f, "NewWebView"), + EmbedderEvent::NewWebView(_, TopLevelBrowsingContextId(webview_id)) => { + write!(f, "NewWebView({webview_id:?})") + }, EmbedderEvent::SendError(..) => write!(f, "SendError"), - EmbedderEvent::CloseWebView(..) => write!(f, "CloseWebView"), - EmbedderEvent::FocusWebView(..) => write!(f, "FocusWebView"), + EmbedderEvent::CloseWebView(TopLevelBrowsingContextId(webview_id)) => { + write!(f, "CloseWebView({webview_id:?})") + }, + EmbedderEvent::MoveResizeWebView(webview_id, _) => { + write!(f, "MoveResizeWebView({webview_id:?})") + }, + EmbedderEvent::ShowWebView(TopLevelBrowsingContextId(webview_id), hide_others) => { + write!(f, "ShowWebView({webview_id:?}, {hide_others})") + }, + EmbedderEvent::HideWebView(TopLevelBrowsingContextId(webview_id)) => { + write!(f, "HideWebView({webview_id:?})") + }, + EmbedderEvent::RaiseWebViewToTop( + TopLevelBrowsingContextId(webview_id), + hide_others, + ) => { + write!(f, "RaiseWebViewToTop({webview_id:?}, {hide_others})") + }, + EmbedderEvent::FocusWebView(TopLevelBrowsingContextId(webview_id)) => { + write!(f, "FocusWebView({webview_id:?})") + }, + EmbedderEvent::BlurWebView => write!(f, "BlurWebView"), EmbedderEvent::ToggleWebRenderDebug(..) => write!(f, "ToggleWebRenderDebug"), EmbedderEvent::CaptureWebRender => write!(f, "CaptureWebRender"), EmbedderEvent::ToggleSamplingProfiler(..) => write!(f, "ToggleSamplingProfiler"), @@ -211,15 +242,20 @@ pub struct EmbedderCoordinates { impl EmbedderCoordinates { /// Get the unflipped viewport rectangle for use with the WebRender API. pub fn get_viewport(&self) -> DeviceIntRect { - DeviceIntRect::from_untyped(&self.viewport.to_untyped()) + self.viewport.clone() } - /// Get the flipped viewport rectangle. This should be used when drawing directly - /// to the framebuffer with OpenGL commands. + /// Flip the given rect. + /// This should be used when drawing directly to the framebuffer with OpenGL commands. + pub fn flip_rect(&self, rect: &DeviceIntRect) -> DeviceIntRect { + let mut result = rect.clone(); + result.min.y = self.framebuffer.height - result.min.y - result.size().height; + result + } + + /// Get the flipped viewport rectangle. + /// This should be used when drawing directly to the framebuffer with OpenGL commands. pub fn get_flipped_viewport(&self) -> DeviceIntRect { - let fb_height = self.framebuffer.height; - let mut view = self.viewport; - view.min.y = fb_height - view.min.y - view.size().height; - DeviceIntRect::from_untyped(&view.to_untyped()) + self.flip_rect(&self.get_viewport()) } } diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 6ce89b92834..70d4452534d 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -1471,6 +1471,48 @@ where } self.handle_panic(top_level_browsing_context_id, error, None); }, + FromCompositorMsg::MoveResizeWebView(top_level_browsing_context_id, rect) => { + if self.webviews.get(top_level_browsing_context_id).is_none() { + return warn!( + "{}: MoveResizeWebView on unknown top-level browsing context", + top_level_browsing_context_id + ); + } + self.compositor_proxy.send(CompositorMsg::MoveResizeWebView( + top_level_browsing_context_id, + rect, + )); + }, + FromCompositorMsg::ShowWebView(webview_id, hide_others) => { + if self.webviews.get(webview_id).is_none() { + return warn!( + "{}: ShowWebView on unknown top-level browsing context", + webview_id + ); + } + self.compositor_proxy + .send(CompositorMsg::ShowWebView(webview_id, hide_others)); + }, + FromCompositorMsg::HideWebView(webview_id) => { + if self.webviews.get(webview_id).is_none() { + return warn!( + "{}: HideWebView on unknown top-level browsing context", + webview_id + ); + } + self.compositor_proxy + .send(CompositorMsg::HideWebView(webview_id)); + }, + FromCompositorMsg::RaiseWebViewToTop(webview_id, hide_others) => { + if self.webviews.get(webview_id).is_none() { + return warn!( + "{}: RaiseWebViewToTop on unknown top-level browsing context", + webview_id + ); + } + self.compositor_proxy + .send(CompositorMsg::RaiseWebViewToTop(webview_id, hide_others)); + }, FromCompositorMsg::FocusWebView(top_level_browsing_context_id) => { if self.webviews.get(top_level_browsing_context_id).is_none() { return warn!("{top_level_browsing_context_id}: FocusWebView on unknown top-level browsing context"); @@ -1480,9 +1522,6 @@ where Some(top_level_browsing_context_id), EmbedderMsg::WebViewFocused(top_level_browsing_context_id), )); - if !cfg!(feature = "multiview") { - self.update_frame_tree_if_focused(top_level_browsing_context_id); - } }, FromCompositorMsg::BlurWebView => { self.webviews.unfocus(); @@ -1539,11 +1578,9 @@ where FromCompositorMsg::SetWebViewThrottled(webview_id, throttled) => { self.set_webview_throttled(webview_id, throttled); }, - FromCompositorMsg::ReadyToPresent(top_level_browsing_context_id) => { - self.embedder_proxy.send(( - Some(top_level_browsing_context_id), - EmbedderMsg::ReadyToPresent, - )); + FromCompositorMsg::ReadyToPresent(webview_ids) => { + self.embedder_proxy + .send((None, EmbedderMsg::ReadyToPresent(webview_ids))); }, FromCompositorMsg::Gamepad(gamepad_event) => { self.handle_gamepad_msg(gamepad_event); @@ -3016,7 +3053,8 @@ where .send((None, EmbedderMsg::WebViewBlurred)); } self.webviews.remove(top_level_browsing_context_id); - // TODO Send the compositor a RemoveWebView event. + self.compositor_proxy + .send(CompositorMsg::RemoveWebView(top_level_browsing_context_id)); self.embedder_proxy.send(( Some(top_level_browsing_context_id), EmbedderMsg::WebViewClosed(top_level_browsing_context_id), @@ -3804,7 +3842,7 @@ where self.notify_history_changed(top_level_browsing_context_id); self.trim_history(top_level_browsing_context_id); - self.update_frame_tree_if_focused(top_level_browsing_context_id); + self.update_webview_in_compositor(top_level_browsing_context_id); } fn update_browsing_context( @@ -4763,7 +4801,7 @@ where } self.notify_history_changed(change.top_level_browsing_context_id); - self.update_frame_tree_if_focused(change.top_level_browsing_context_id); + self.update_webview_in_compositor(change.top_level_browsing_context_id); } fn focused_browsing_context_is_descendant_of( @@ -5376,24 +5414,15 @@ where } /// Send the frame tree for the given webview to the compositor. - fn update_frame_tree_if_focused( - &mut self, - top_level_browsing_context_id: TopLevelBrowsingContextId, - ) { - // Only send the frame tree if the given webview is focused. - if let Some(focused_webview_id) = self.webviews.focused_webview().map(|(id, _)| id) { - if top_level_browsing_context_id != focused_webview_id { - return; - } - } + fn update_webview_in_compositor(&mut self, webview_id: WebViewId) { // Note that this function can panic, due to ipc-channel creation failure. // avoiding this panic would require a mechanism for dealing // with low-resource scenarios. - let browsing_context_id = BrowsingContextId::from(top_level_browsing_context_id); + let browsing_context_id = BrowsingContextId::from(webview_id); if let Some(frame_tree) = self.browsing_context_to_sendable(browsing_context_id) { debug!("{}: Sending frame tree", browsing_context_id); self.compositor_proxy - .send(CompositorMsg::SetFrameTree(frame_tree)); + .send(CompositorMsg::CreateOrUpdateWebView(frame_tree)); } } diff --git a/components/constellation/tracing.rs b/components/constellation/tracing.rs index babb409747a..040a8bd3269 100644 --- a/components/constellation/tracing.rs +++ b/components/constellation/tracing.rs @@ -59,37 +59,41 @@ mod from_compositor { fn log_target(&self) -> &'static str { match self { Self::Exit => target!("Exit"), - Self::GetBrowsingContext(_, _) => target!("GetBrowsingContext"), - Self::GetPipeline(_, _) => target!("GetPipeline"), - Self::GetFocusTopLevelBrowsingContext(_) => { + Self::GetBrowsingContext(..) => target!("GetBrowsingContext"), + Self::GetPipeline(..) => target!("GetPipeline"), + Self::GetFocusTopLevelBrowsingContext(..) => { target!("GetFocusTopLevelBrowsingContext") }, - Self::IsReadyToSaveImage(_) => target!("IsReadyToSaveImage"), - Self::Keyboard(_) => target!("Keyboard"), - Self::AllowNavigationResponse(_, _) => target!("AllowNavigationResponse"), - Self::LoadUrl(_, _) => target!("LoadUrl"), + Self::IsReadyToSaveImage(..) => target!("IsReadyToSaveImage"), + Self::Keyboard(..) => target!("Keyboard"), + Self::AllowNavigationResponse(..) => target!("AllowNavigationResponse"), + Self::LoadUrl(..) => target!("LoadUrl"), Self::ClearCache => target!("ClearCache"), - Self::TraverseHistory(_, _) => target!("TraverseHistory"), - Self::WindowSize(_, _, _) => target!("WindowSize"), - Self::TickAnimation(_, _) => target!("TickAnimation"), - Self::WebDriverCommand(_) => target!("WebDriverCommand"), - Self::Reload(_) => target!("Reload"), - Self::LogEntry(_, _, _) => target!("LogEntry"), - Self::NewWebView(_, _) => target!("NewWebView"), - Self::CloseWebView(_) => target!("CloseWebView"), - Self::SendError(_, _) => target!("SendError"), - Self::FocusWebView(_) => target!("FocusWebView"), + Self::TraverseHistory(..) => target!("TraverseHistory"), + Self::WindowSize(..) => target!("WindowSize"), + Self::TickAnimation(..) => target!("TickAnimation"), + Self::WebDriverCommand(..) => target!("WebDriverCommand"), + Self::Reload(..) => target!("Reload"), + Self::LogEntry(..) => target!("LogEntry"), + Self::NewWebView(..) => target!("NewWebView"), + Self::CloseWebView(..) => target!("CloseWebView"), + Self::SendError(..) => target!("SendError"), + Self::MoveResizeWebView(..) => target!("MoveResizeWebView"), + Self::ShowWebView(..) => target!("ShowWebView"), + Self::HideWebView(..) => target!("HideWebView"), + Self::RaiseWebViewToTop(..) => target!("RaiseWebViewToTop"), + Self::FocusWebView(..) => target!("FocusWebView"), Self::BlurWebView => target!("BlurWebView"), Self::ForwardEvent(_, event) => event.log_target(), - Self::SetCursor(_) => target!("SetCursor"), - Self::EnableProfiler(_, _) => target!("EnableProfiler"), + Self::SetCursor(..) => target!("SetCursor"), + Self::EnableProfiler(..) => target!("EnableProfiler"), Self::DisableProfiler => target!("DisableProfiler"), Self::ExitFullScreen(_) => target!("ExitFullScreen"), Self::MediaSessionAction(_) => target!("MediaSessionAction"), Self::SetWebViewThrottled(_, _) => target!("SetWebViewThrottled"), Self::IMEDismissed => target!("IMEDismissed"), - Self::ReadyToPresent(_) => target!("ReadyToPresent"), - Self::Gamepad(_) => target!("Gamepad"), + Self::ReadyToPresent(..) => target!("ReadyToPresent"), + Self::Gamepad(..) => target!("Gamepad"), } } } @@ -102,15 +106,15 @@ mod from_compositor { }; } match self { - Self::ResizeEvent(_, _) => target_variant!("ResizeEvent"), - Self::MouseButtonEvent(_, _, _, _, _, _) => target_variant!("MouseButtonEvent"), - Self::MouseMoveEvent(_, _, _) => target_variant!("MouseMoveEvent"), - Self::TouchEvent(_, _, _, _) => target_variant!("TouchEvent"), - Self::WheelEvent(_, _, _) => target_variant!("WheelEvent"), - Self::KeyboardEvent(_) => target_variant!("KeyboardEvent"), - Self::CompositionEvent(_) => target_variant!("CompositionEvent"), + Self::ResizeEvent(..) => target_variant!("ResizeEvent"), + Self::MouseButtonEvent(..) => target_variant!("MouseButtonEvent"), + Self::MouseMoveEvent(..) => target_variant!("MouseMoveEvent"), + Self::TouchEvent(..) => target_variant!("TouchEvent"), + Self::WheelEvent(..) => target_variant!("WheelEvent"), + Self::KeyboardEvent(..) => target_variant!("KeyboardEvent"), + Self::CompositionEvent(..) => target_variant!("CompositionEvent"), Self::IMEDismissedEvent => target_variant!("IMEDismissedEvent"), - Self::GamepadEvent(_) => target_variant!("GamepadEvent"), + Self::GamepadEvent(..) => target_variant!("GamepadEvent"), } } } @@ -128,65 +132,65 @@ mod from_script { impl LogTarget for script_traits::ScriptMsg { fn log_target(&self) -> &'static str { match self { - Self::CompleteMessagePortTransfer(_, _) => target!("CompleteMessagePortTransfer"), - Self::MessagePortTransferResult(_, _, _) => target!("MessagePortTransferResult"), - Self::NewMessagePort(_, _) => target!("NewMessagePort"), - Self::NewMessagePortRouter(_, _) => target!("NewMessagePortRouter"), - Self::RemoveMessagePortRouter(_) => target!("RemoveMessagePortRouter"), - Self::RerouteMessagePort(_, _) => target!("RerouteMessagePort"), - Self::MessagePortShipped(_) => target!("MessagePortShipped"), - Self::RemoveMessagePort(_) => target!("RemoveMessagePort"), - Self::EntanglePorts(_, _) => target!("EntanglePorts"), - Self::NewBroadcastChannelRouter(_, _, _) => target!("NewBroadcastChannelRouter"), - Self::RemoveBroadcastChannelRouter(_, _) => target!("RemoveBroadcastChannelRouter"), - Self::NewBroadcastChannelNameInRouter(_, _, _) => { + Self::CompleteMessagePortTransfer(..) => target!("CompleteMessagePortTransfer"), + Self::MessagePortTransferResult(..) => target!("MessagePortTransferResult"), + Self::NewMessagePort(..) => target!("NewMessagePort"), + Self::NewMessagePortRouter(..) => target!("NewMessagePortRouter"), + Self::RemoveMessagePortRouter(..) => target!("RemoveMessagePortRouter"), + Self::RerouteMessagePort(..) => target!("RerouteMessagePort"), + Self::MessagePortShipped(..) => target!("MessagePortShipped"), + Self::RemoveMessagePort(..) => target!("RemoveMessagePort"), + Self::EntanglePorts(..) => target!("EntanglePorts"), + Self::NewBroadcastChannelRouter(..) => target!("NewBroadcastChannelRouter"), + Self::RemoveBroadcastChannelRouter(..) => target!("RemoveBroadcastChannelRouter"), + Self::NewBroadcastChannelNameInRouter(..) => { target!("NewBroadcastChannelNameInRouter") }, - Self::RemoveBroadcastChannelNameInRouter(_, _, _) => { + Self::RemoveBroadcastChannelNameInRouter(..) => { target!("RemoveBroadcastChannelNameInRouter") }, - Self::ScheduleBroadcast(_, _) => target!("ScheduleBroadcast"), + Self::ScheduleBroadcast(..) => target!("ScheduleBroadcast"), Self::ForwardToEmbedder(msg) => msg.log_target(), - Self::InitiateNavigateRequest(_, _) => target!("InitiateNavigateRequest"), - Self::BroadcastStorageEvent(_, _, _, _, _) => target!("BroadcastStorageEvent"), - Self::ChangeRunningAnimationsState(_) => target!("ChangeRunningAnimationsState"), - Self::CreateCanvasPaintThread(_, _) => target!("CreateCanvasPaintThread"), + Self::InitiateNavigateRequest(..) => target!("InitiateNavigateRequest"), + Self::BroadcastStorageEvent(..) => target!("BroadcastStorageEvent"), + Self::ChangeRunningAnimationsState(..) => target!("ChangeRunningAnimationsState"), + Self::CreateCanvasPaintThread(..) => target!("CreateCanvasPaintThread"), Self::Focus => target!("Focus"), - Self::GetTopForBrowsingContext(_, _) => target!("GetTopForBrowsingContext"), - Self::GetBrowsingContextInfo(_, _) => target!("GetBrowsingContextInfo"), - Self::GetChildBrowsingContextId(_, _, _) => target!("GetChildBrowsingContextId"), + Self::GetTopForBrowsingContext(..) => target!("GetTopForBrowsingContext"), + Self::GetBrowsingContextInfo(..) => target!("GetBrowsingContextInfo"), + Self::GetChildBrowsingContextId(..) => target!("GetChildBrowsingContextId"), Self::LoadComplete => target!("LoadComplete"), - Self::LoadUrl(_, _) => target!("LoadUrl"), + Self::LoadUrl(..) => target!("LoadUrl"), Self::AbortLoadUrl => target!("AbortLoadUrl"), Self::PostMessage { .. } => target!("PostMessage"), - Self::NavigatedToFragment(_, _) => target!("NavigatedToFragment"), - Self::TraverseHistory(_) => target!("TraverseHistory"), - Self::PushHistoryState(_, _) => target!("PushHistoryState"), - Self::ReplaceHistoryState(_, _) => target!("ReplaceHistoryState"), - Self::JointSessionHistoryLength(_) => target!("JointSessionHistoryLength"), - Self::RemoveIFrame(_, _) => target!("RemoveIFrame"), - Self::SetThrottledComplete(_) => target!("SetThrottledComplete"), - Self::ScriptLoadedURLInIFrame(_) => target!("ScriptLoadedURLInIFrame"), - Self::ScriptNewIFrame(_) => target!("ScriptNewIFrame"), - Self::ScriptNewAuxiliary(_) => target!("ScriptNewAuxiliary"), + Self::NavigatedToFragment(..) => target!("NavigatedToFragment"), + Self::TraverseHistory(..) => target!("TraverseHistory"), + Self::PushHistoryState(..) => target!("PushHistoryState"), + Self::ReplaceHistoryState(..) => target!("ReplaceHistoryState"), + Self::JointSessionHistoryLength(..) => target!("JointSessionHistoryLength"), + Self::RemoveIFrame(..) => target!("RemoveIFrame"), + Self::SetThrottledComplete(..) => target!("SetThrottledComplete"), + Self::ScriptLoadedURLInIFrame(..) => target!("ScriptLoadedURLInIFrame"), + Self::ScriptNewIFrame(..) => target!("ScriptNewIFrame"), + Self::ScriptNewAuxiliary(..) => target!("ScriptNewAuxiliary"), Self::ActivateDocument => target!("ActivateDocument"), - Self::SetDocumentState(_) => target!("SetDocumentState"), - Self::SetLayoutEpoch(_, _) => target!("SetLayoutEpoch"), - Self::SetFinalUrl(_) => target!("SetFinalUrl"), - Self::TouchEventProcessed(_) => target!("TouchEventProcessed"), - Self::LogEntry(_, _) => target!("LogEntry"), + Self::SetDocumentState(..) => target!("SetDocumentState"), + Self::SetLayoutEpoch(..) => target!("SetLayoutEpoch"), + Self::SetFinalUrl(..) => target!("SetFinalUrl"), + Self::TouchEventProcessed(..) => target!("TouchEventProcessed"), + Self::LogEntry(..) => target!("LogEntry"), Self::DiscardDocument => target!("DiscardDocument"), Self::DiscardTopLevelBrowsingContext => target!("DiscardTopLevelBrowsingContext"), Self::PipelineExited => target!("PipelineExited"), - Self::ForwardDOMMessage(_, _) => target!("ForwardDOMMessage"), - Self::ScheduleJob(_) => target!("ScheduleJob"), - Self::GetClientWindow(_) => target!("GetClientWindow"), - Self::GetScreenSize(_) => target!("GetScreenSize"), - Self::GetScreenAvailSize(_) => target!("GetScreenAvailSize"), - Self::MediaSessionEvent(_, _) => target!("MediaSessionEvent"), - Self::RequestAdapter(_, _, _) => target!("RequestAdapter"), - Self::GetWebGPUChan(_) => target!("GetWebGPUChan"), - Self::TitleChanged(_, _) => target!("TitleChanged"), + Self::ForwardDOMMessage(..) => target!("ForwardDOMMessage"), + Self::ScheduleJob(..) => target!("ScheduleJob"), + Self::GetClientWindow(..) => target!("GetClientWindow"), + Self::GetScreenSize(..) => target!("GetScreenSize"), + Self::GetScreenAvailSize(..) => target!("GetScreenAvailSize"), + Self::MediaSessionEvent(..) => target!("MediaSessionEvent"), + Self::RequestAdapter(..) => target!("RequestAdapter"), + Self::GetWebGPUChan(..) => target!("GetWebGPUChan"), + Self::TitleChanged(..) => target!("TitleChanged"), } } } @@ -199,43 +203,43 @@ mod from_script { }; } match self { - Self::Status(_) => target_variant!("Status"), - Self::ChangePageTitle(_) => target_variant!("ChangePageTitle"), - Self::MoveTo(_) => target_variant!("MoveTo"), - Self::ResizeTo(_) => target_variant!("ResizeTo"), - Self::Prompt(_, _) => target_variant!("Prompt"), - Self::ShowContextMenu(_, _, _) => target_variant!("ShowContextMenu"), - Self::AllowNavigationRequest(_, _) => target_variant!("AllowNavigationRequest"), - Self::AllowOpeningWebView(_) => target_variant!("AllowOpeningWebView"), - Self::WebViewOpened(_) => target_variant!("WebViewOpened"), - Self::WebViewClosed(_) => target_variant!("WebViewClosed"), - Self::WebViewFocused(_) => target_variant!("WebViewFocused"), + Self::Status(..) => target_variant!("Status"), + Self::ChangePageTitle(..) => target_variant!("ChangePageTitle"), + Self::MoveTo(..) => target_variant!("MoveTo"), + Self::ResizeTo(..) => target_variant!("ResizeTo"), + Self::Prompt(..) => target_variant!("Prompt"), + Self::ShowContextMenu(..) => target_variant!("ShowContextMenu"), + Self::AllowNavigationRequest(..) => target_variant!("AllowNavigationRequest"), + Self::AllowOpeningWebView(..) => target_variant!("AllowOpeningWebView"), + Self::WebViewOpened(..) => target_variant!("WebViewOpened"), + Self::WebViewClosed(..) => target_variant!("WebViewClosed"), + Self::WebViewFocused(..) => target_variant!("WebViewFocused"), Self::WebViewBlurred => target_variant!("WebViewBlurred"), - Self::AllowUnload(_) => target_variant!("AllowUnload"), - Self::Keyboard(_) => target_variant!("Keyboard"), - Self::GetClipboardContents(_) => target_variant!("GetClipboardContents"), - Self::SetClipboardContents(_) => target_variant!("SetClipboardContents"), - Self::SetCursor(_) => target_variant!("SetCursor"), - Self::NewFavicon(_) => target_variant!("NewFavicon"), + Self::AllowUnload(..) => target_variant!("AllowUnload"), + Self::Keyboard(..) => target_variant!("Keyboard"), + Self::GetClipboardContents(..) => target_variant!("GetClipboardContents"), + Self::SetClipboardContents(..) => target_variant!("SetClipboardContents"), + Self::SetCursor(..) => target_variant!("SetCursor"), + Self::NewFavicon(..) => target_variant!("NewFavicon"), Self::HeadParsed => target_variant!("HeadParsed"), - Self::HistoryChanged(_, _) => target_variant!("HistoryChanged"), - Self::SetFullscreenState(_) => target_variant!("SetFullscreenState"), + Self::HistoryChanged(..) => target_variant!("HistoryChanged"), + Self::SetFullscreenState(..) => target_variant!("SetFullscreenState"), Self::LoadStart => target_variant!("LoadStart"), Self::LoadComplete => target_variant!("LoadComplete"), - Self::Panic(_, _) => target_variant!("Panic"), - Self::GetSelectedBluetoothDevice(_, _) => { + Self::Panic(..) => target_variant!("Panic"), + Self::GetSelectedBluetoothDevice(..) => { target_variant!("GetSelectedBluetoothDevice") }, - Self::SelectFiles(_, _, _) => target_variant!("SelectFiles"), - Self::PromptPermission(_, _) => target_variant!("PromptPermission"), - Self::ShowIME(_, _, _, _) => target_variant!("ShowIME"), + Self::SelectFiles(..) => target_variant!("SelectFiles"), + Self::PromptPermission(..) => target_variant!("PromptPermission"), + Self::ShowIME(..) => target_variant!("ShowIME"), Self::HideIME => target_variant!("HideIME"), Self::Shutdown => target_variant!("Shutdown"), - Self::ReportProfile(_) => target_variant!("ReportProfile"), - Self::MediaSessionEvent(_) => target_variant!("MediaSessionEvent"), - Self::OnDevtoolsStarted(_, _) => target_variant!("OnDevtoolsStarted"), - Self::ReadyToPresent => target_variant!("ReadyToPresent"), - Self::EventDelivered(_) => target_variant!("EventDelivered"), + Self::ReportProfile(..) => target_variant!("ReportProfile"), + Self::MediaSessionEvent(..) => target_variant!("MediaSessionEvent"), + Self::OnDevtoolsStarted(..) => target_variant!("OnDevtoolsStarted"), + Self::ReadyToPresent(..) => target_variant!("ReadyToPresent"), + Self::EventDelivered(..) => target_variant!("EventDelivered"), } } } @@ -253,8 +257,8 @@ mod from_layout { impl LogTarget for script_traits::LayoutMsg { fn log_target(&self) -> &'static str { match self { - Self::IFrameSizes(_) => target!("IFrameSizes"), - Self::PendingPaintMetric(_, _) => target!("PendingPaintMetric"), + Self::IFrameSizes(..) => target!("IFrameSizes"), + Self::PendingPaintMetric(..) => target!("PendingPaintMetric"), } } } diff --git a/components/constellation/webview.rs b/components/constellation/webview.rs index cb2b85de468..ddd51e31848 100644 --- a/components/constellation/webview.rs +++ b/components/constellation/webview.rs @@ -101,12 +101,12 @@ mod test { use msg::constellation_msg::{ BrowsingContextId, BrowsingContextIndex, PipelineNamespace, PipelineNamespaceId, - TopLevelBrowsingContextId, + TopLevelBrowsingContextId, WebViewId, }; use crate::webview::WebViewManager; - fn top_level_id(namespace_id: u32, index: u32) -> TopLevelBrowsingContextId { + fn id(namespace_id: u32, index: u32) -> WebViewId { TopLevelBrowsingContextId(BrowsingContextId { namespace_id: PipelineNamespaceId(namespace_id), index: BrowsingContextIndex(NonZeroU32::new(index).expect("Incorrect test case")), @@ -115,7 +115,7 @@ mod test { fn webviews_sorted( webviews: &WebViewManager, - ) -> Vec<(TopLevelBrowsingContextId, WebView)> { + ) -> Vec<(WebViewId, WebView)> { let mut keys = webviews.webviews.keys().collect::>(); keys.sort(); keys.iter() @@ -138,59 +138,59 @@ mod test { let mut webviews = WebViewManager::default(); // add() adds the webview to the map, but does not focus it. - webviews.add(TopLevelBrowsingContextId::new(), 'a'); - webviews.add(TopLevelBrowsingContextId::new(), 'b'); - webviews.add(TopLevelBrowsingContextId::new(), 'c'); + webviews.add(WebViewId::new(), 'a'); + webviews.add(WebViewId::new(), 'b'); + webviews.add(WebViewId::new(), 'c'); assert_eq!( webviews_sorted(&webviews), - vec![ - (top_level_id(0, 1), 'a'), - (top_level_id(0, 2), 'b'), - (top_level_id(0, 3), 'c'), - ] + vec![(id(0, 1), 'a'), (id(0, 2), 'b'), (id(0, 3), 'c'),] ); assert!(webviews.focus_order.is_empty()); assert_eq!(webviews.is_focused, false); // focus() makes the given webview the latest in focus order. - webviews.focus(top_level_id(0, 2)); - assert_eq!(webviews.focus_order, vec![top_level_id(0, 2)]); + webviews.focus(id(0, 2)); + assert_eq!(webviews.focus_order, vec![id(0, 2)]); assert_eq!(webviews.is_focused, true); - webviews.focus(top_level_id(0, 1)); - assert_eq!( - webviews.focus_order, - vec![top_level_id(0, 2), top_level_id(0, 1)] - ); + webviews.focus(id(0, 1)); + assert_eq!(webviews.focus_order, vec![id(0, 2), id(0, 1)]); assert_eq!(webviews.is_focused, true); - webviews.focus(top_level_id(0, 3)); - assert_eq!( - webviews.focus_order, - vec![top_level_id(0, 2), top_level_id(0, 1), top_level_id(0, 3)] - ); + webviews.focus(id(0, 3)); + assert_eq!(webviews.focus_order, vec![id(0, 2), id(0, 1), id(0, 3)]); assert_eq!(webviews.is_focused, true); // unfocus() clears the “is focused” flag, but does not touch the focus order. webviews.unfocus(); - assert_eq!( - webviews.focus_order, - vec![top_level_id(0, 2), top_level_id(0, 1), top_level_id(0, 3)] - ); + assert_eq!(webviews.focus_order, vec![id(0, 2), id(0, 1), id(0, 3)]); assert_eq!(webviews.is_focused, false); // focus() avoids duplicates in focus order, when the given webview has been focused before. - webviews.focus(top_level_id(0, 1)); - assert_eq!( - webviews.focus_order, - vec![top_level_id(0, 2), top_level_id(0, 3), top_level_id(0, 1)] - ); + webviews.focus(id(0, 1)); + assert_eq!(webviews.focus_order, vec![id(0, 2), id(0, 3), id(0, 1)]); assert_eq!(webviews.is_focused, true); // remove() clears the “is focused” flag iff the given webview was focused. - webviews.remove(top_level_id(0, 2)); + webviews.remove(id(1, 1)); assert_eq!(webviews.is_focused, true); - webviews.remove(top_level_id(0, 1)); + webviews.remove(id(1, 2)); + assert_eq!(webviews.is_focused, true); + webviews.remove(id(2, 1)); + assert_eq!(webviews.is_focused, true); + webviews.remove(id(2, 2)); + assert_eq!(webviews.is_focused, true); + webviews.remove(id(2, 3)); + assert_eq!(webviews.is_focused, true); + webviews.remove(id(2, 4)); + assert_eq!(webviews.is_focused, true); + webviews.remove(id(3, 1)); + assert_eq!(webviews.is_focused, true); + webviews.remove(id(4, 1)); + assert_eq!(webviews.is_focused, true); + webviews.remove(id(0, 2)); + assert_eq!(webviews.is_focused, true); + webviews.remove(id(0, 1)); assert_eq!(webviews.is_focused, false); - webviews.remove(top_level_id(0, 3)); + webviews.remove(id(0, 3)); assert_eq!(webviews.is_focused, false); // remove() removes the given webview from both the map and the focus order. diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 55c12e52ba9..b98b3aeb386 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -30,6 +30,7 @@ use bluetooth_traits::BluetoothRequest; use canvas::canvas_paint_thread::{self, CanvasPaintThread}; use canvas::WebGLComm; use canvas_traits::webgl::WebGLThreads; +use compositing::webview::UnknownWebView; use compositing::windowing::{EmbedderEvent, EmbedderMethods, WindowMethods}; use compositing::{CompositeTarget, IOCompositor, InitialCompositorState, ShutdownState}; use compositing_traits::{ @@ -598,7 +599,7 @@ where self.compositor.composite(); }, - EmbedderEvent::Resize => { + EmbedderEvent::WindowResize => { return self.compositor.on_resize_window_event(); }, EmbedderEvent::InvalidateNativeSurface => { @@ -761,6 +762,33 @@ where } }, + EmbedderEvent::MoveResizeWebView(webview_id, rect) => { + self.compositor.move_resize_webview(webview_id, rect); + }, + EmbedderEvent::ShowWebView(webview_id, hide_others) => { + if let Err(UnknownWebView(webview_id)) = + self.compositor.show_webview(webview_id, hide_others) + { + warn!("{webview_id}: ShowWebView on unknown webview id"); + } + }, + EmbedderEvent::HideWebView(webview_id) => { + if let Err(UnknownWebView(webview_id)) = self.compositor.hide_webview(webview_id) { + warn!("{webview_id}: HideWebView on unknown webview id"); + } + }, + EmbedderEvent::RaiseWebViewToTop(webview_id, hide_others) => { + if let Err(UnknownWebView(webview_id)) = self + .compositor + .raise_webview_to_top(webview_id, hide_others) + { + warn!("{webview_id}: RaiseWebViewToTop on unknown webview id"); + } + }, + EmbedderEvent::BlurWebView => { + self.send_to_constellation(ConstellationMsg::BlurWebView); + }, + EmbedderEvent::SendError(top_level_browsing_context_id, e) => { let msg = ConstellationMsg::SendError(top_level_browsing_context_id, e); if let Err(e) = self.constellation_chan.send(msg) { @@ -801,6 +829,13 @@ where false } + fn send_to_constellation(&self, msg: ConstellationMsg) { + let variant_name = msg.variant_name(); + if let Err(e) = self.constellation_chan.send(msg) { + warn!("Sending {variant_name} to constellation failed: {e:?}"); + } + } + fn receive_messages(&mut self) { while let Some((top_level_browsing_context, msg)) = self.embedder_receiver.try_recv_embedder_msg() @@ -882,10 +917,6 @@ where self.compositor.present(); } - pub fn recomposite(&mut self) { - self.compositor.composite(); - } - /// Return the OpenGL framebuffer name of the most-recently-completed frame when compositing to /// [`CompositeTarget::Fbo`], or None otherwise. pub fn offscreen_framebuffer_id(&self) -> Option { diff --git a/components/shared/compositing/constellation_msg.rs b/components/shared/compositing/constellation_msg.rs index 8204ec6d2e3..64d593ee08c 100644 --- a/components/shared/compositing/constellation_msg.rs +++ b/components/shared/compositing/constellation_msg.rs @@ -11,13 +11,14 @@ use gfx_traits::Epoch; use ipc_channel::ipc::IpcSender; use keyboard_types::KeyboardEvent; use msg::constellation_msg::{ - BrowsingContextId, PipelineId, TopLevelBrowsingContextId, TraversalDirection, + BrowsingContextId, PipelineId, TopLevelBrowsingContextId, TraversalDirection, WebViewId, }; use script_traits::{ AnimationTickType, CompositorEvent, GamepadEvent, LogEntry, MediaSessionActionType, WebDriverCommandMsg, WindowSizeData, WindowSizeType, }; use servo_url::ServoUrl; +use webrender_api::units::DeviceRect; /// Messages to the constellation. pub enum ConstellationMsg { @@ -60,9 +61,17 @@ pub enum ConstellationMsg { CloseWebView(TopLevelBrowsingContextId), /// Panic a top level browsing context. SendError(Option, String), - /// Make a top-level browsing context focused. + /// Move and/or resize a webview to the given rect. + MoveResizeWebView(TopLevelBrowsingContextId, DeviceRect), + /// Start painting a webview, and optionally stop painting all others. + ShowWebView(TopLevelBrowsingContextId, bool), + /// Stop painting a webview. + HideWebView(TopLevelBrowsingContextId), + /// Start painting a webview on top of all others, and optionally stop painting all others. + RaiseWebViewToTop(TopLevelBrowsingContextId, bool), + /// Make a webview focused. FocusWebView(TopLevelBrowsingContextId), - /// Make none of the top-level browsing contexts focused. + /// Make none of the webviews focused. BlurWebView, /// Forward an event to the script task of the given pipeline. ForwardEvent(PipelineId, CompositorEvent), @@ -80,16 +89,23 @@ pub enum ConstellationMsg { SetWebViewThrottled(TopLevelBrowsingContextId, bool), /// Virtual keyboard was dismissed IMEDismissed, - /// Compositing done, but external code needs to present. - ReadyToPresent(TopLevelBrowsingContextId), + /// Notify the embedder that it needs to present a new frame. + ReadyToPresent(Vec), /// Gamepad state has changed Gamepad(GamepadEvent), } impl fmt::Debug for ConstellationMsg { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "ConstellationMsg::{}", self.variant_name()) + } +} + +impl ConstellationMsg { + /// Return the variant name, for error logging that happens after the message is consumed. + pub fn variant_name(&self) -> &'static str { use self::ConstellationMsg::*; - let variant = match *self { + match *self { Exit => "Exit", GetBrowsingContext(..) => "GetBrowsingContext", GetPipeline(..) => "GetPipeline", @@ -106,6 +122,10 @@ impl fmt::Debug for ConstellationMsg { LogEntry(..) => "LogEntry", NewWebView(..) => "NewWebView", CloseWebView(..) => "CloseWebView", + MoveResizeWebView(..) => "MoveResizeWebView", + ShowWebView(..) => "ShowWebView", + HideWebView(..) => "HideWebView", + RaiseWebViewToTop(..) => "RaiseWebViewToTop", FocusWebView(..) => "FocusWebView", BlurWebView => "BlurWebView", SendError(..) => "SendError", @@ -120,7 +140,6 @@ impl fmt::Debug for ConstellationMsg { ClearCache => "ClearCache", ReadyToPresent(..) => "ReadyToPresent", Gamepad(..) => "Gamepad", - }; - write!(formatter, "ConstellationMsg::{}", variant) + } } } diff --git a/components/shared/compositing/lib.rs b/components/shared/compositing/lib.rs index 8ab45b374ed..9c759bc4acf 100644 --- a/components/shared/compositing/lib.rs +++ b/components/shared/compositing/lib.rs @@ -24,7 +24,7 @@ use script_traits::{ ScriptToCompositorMsg, }; use style_traits::CSSPixel; -use webrender_api::units::{DeviceIntPoint, DeviceIntSize}; +use webrender_api::units::{DeviceIntPoint, DeviceIntSize, DeviceRect}; use webrender_api::{self, FontInstanceKey, FontKey, ImageKey}; /// Sends messages to the compositor. @@ -73,8 +73,18 @@ pub enum CompositorMsg { ShutdownComplete, /// Alerts the compositor that the given pipeline has changed whether it is running animations. ChangeRunningAnimationsState(PipelineId, AnimationState), - /// Replaces the current frame tree, typically called during main frame navigation. - SetFrameTree(SendableFrameTree), + /// Create or update a webview, given its frame tree. + CreateOrUpdateWebView(SendableFrameTree), + /// Remove a webview. + RemoveWebView(TopLevelBrowsingContextId), + /// Move and/or resize a webview to the given rect. + MoveResizeWebView(TopLevelBrowsingContextId, DeviceRect), + /// Start painting a webview, and optionally stop painting all others. + ShowWebView(TopLevelBrowsingContextId, bool), + /// Stop painting a webview. + HideWebView(TopLevelBrowsingContextId), + /// Start painting a webview on top of all others, and optionally stop painting all others. + RaiseWebViewToTop(TopLevelBrowsingContextId, bool), /// Script has handled a touch event, and either prevented or allowed default actions. TouchEventProcessed(EventResult), /// Composite to a PNG file and return the Image over a passed channel. @@ -153,7 +163,12 @@ impl Debug for CompositorMsg { CompositorMsg::ChangeRunningAnimationsState(_, state) => { write!(f, "ChangeRunningAnimationsState({:?})", state) }, - CompositorMsg::SetFrameTree(..) => write!(f, "SetFrameTree"), + CompositorMsg::CreateOrUpdateWebView(..) => write!(f, "CreateOrUpdateWebView"), + CompositorMsg::RemoveWebView(..) => write!(f, "RemoveWebView"), + CompositorMsg::MoveResizeWebView(..) => write!(f, "MoveResizeWebView"), + CompositorMsg::ShowWebView(..) => write!(f, "ShowWebView"), + CompositorMsg::HideWebView(..) => write!(f, "HideWebView"), + CompositorMsg::RaiseWebViewToTop(..) => write!(f, "RaiseWebViewToTop"), CompositorMsg::TouchEventProcessed(..) => write!(f, "TouchEventProcessed"), CompositorMsg::CreatePng(..) => write!(f, "CreatePng"), CompositorMsg::IsReadyToSaveImageReply(..) => write!(f, "IsReadyToSaveImageReply"), diff --git a/components/shared/embedder/lib.rs b/components/shared/embedder/lib.rs index da6eca45291..0acf7d01ce5 100644 --- a/components/shared/embedder/lib.rs +++ b/components/shared/embedder/lib.rs @@ -10,7 +10,7 @@ use crossbeam_channel::{Receiver, Sender}; use ipc_channel::ipc::IpcSender; use keyboard_types::KeyboardEvent; use log::warn; -use msg::constellation_msg::{InputMethodType, PipelineId, TopLevelBrowsingContextId}; +use msg::constellation_msg::{InputMethodType, PipelineId, TopLevelBrowsingContextId, WebViewId}; use num_derive::FromPrimitive; use serde::{Deserialize, Serialize}; use servo_url::ServoUrl; @@ -156,13 +156,13 @@ pub enum EmbedderMsg { AllowNavigationRequest(PipelineId, ServoUrl), /// Whether or not to allow script to open a new tab/browser AllowOpeningWebView(IpcSender), - /// A browser was created + /// A webview was created. WebViewOpened(TopLevelBrowsingContextId), - /// A browser was destroyed + /// A webview was destroyed. WebViewClosed(TopLevelBrowsingContextId), - /// A browser gained focus for keyboard events + /// A webview gained focus for keyboard events. WebViewFocused(TopLevelBrowsingContextId), - /// All browsers lost focus for keyboard events + /// All webviews lost focus for keyboard events. WebViewBlurred, /// Wether or not to unload a document AllowUnload(IpcSender), @@ -210,8 +210,8 @@ pub enum EmbedderMsg { MediaSessionEvent(MediaSessionEvent), /// Report the status of Devtools Server with a token that can be used to bypass the permission prompt. OnDevtoolsStarted(Result, String), - /// Compositing done, but external code needs to present. - ReadyToPresent, + /// Notify the embedder that it needs to present a new frame. + ReadyToPresent(Vec), /// The given event was delivered to a pipeline in the given browser. EventDelivered(CompositorEventVariant), } @@ -261,12 +261,12 @@ impl Debug for EmbedderMsg { EmbedderMsg::WebViewOpened(..) => write!(f, "WebViewOpened"), EmbedderMsg::WebViewClosed(..) => write!(f, "WebViewClosed"), EmbedderMsg::WebViewFocused(..) => write!(f, "WebViewFocused"), - EmbedderMsg::WebViewBlurred => write!(f, "WebViewUnfocused"), + EmbedderMsg::WebViewBlurred => write!(f, "WebViewBlurred"), EmbedderMsg::ReportProfile(..) => write!(f, "ReportProfile"), EmbedderMsg::MediaSessionEvent(..) => write!(f, "MediaSessionEvent"), EmbedderMsg::OnDevtoolsStarted(..) => write!(f, "OnDevtoolsStarted"), EmbedderMsg::ShowContextMenu(..) => write!(f, "ShowContextMenu"), - EmbedderMsg::ReadyToPresent => write!(f, "ReadyToPresent"), + EmbedderMsg::ReadyToPresent(..) => write!(f, "ReadyToPresent"), EmbedderMsg::EventDelivered(..) => write!(f, "HitTestedEvent"), } } diff --git a/ports/jniapi/src/simpleservo.rs b/ports/jniapi/src/simpleservo.rs index cc56141cbba..b4129aa6eb0 100644 --- a/ports/jniapi/src/simpleservo.rs +++ b/ports/jniapi/src/simpleservo.rs @@ -435,7 +435,7 @@ impl ServoGlue { pub fn resize(&mut self, coordinates: Coordinates) -> Result<(), &'static str> { info!("resize"); *self.callbacks.coordinates.borrow_mut() = coordinates; - self.process_event(EmbedderEvent::Resize) + self.process_event(EmbedderEvent::WindowResize) } /// Start scrolling. @@ -818,7 +818,7 @@ impl ServoGlue { EmbedderMsg::Panic(reason, backtrace) => { self.callbacks.host_callbacks.on_panic(reason, backtrace); }, - EmbedderMsg::ReadyToPresent => { + EmbedderMsg::ReadyToPresent(_webview_ids) => { need_present = true; }, EmbedderMsg::Status(..) | diff --git a/ports/servoshell/app.rs b/ports/servoshell/app.rs index 3e3576e98da..4ff46c0cee6 100644 --- a/ports/servoshell/app.rs +++ b/ports/servoshell/app.rs @@ -124,17 +124,15 @@ impl App { if let Some(mut minibrowser) = app.minibrowser() { // Servo is not yet initialised, so there is no `servo_framebuffer_id`. - minibrowser.update(window.winit_window().unwrap(), None, "init"); + minibrowser.update( + window.winit_window().unwrap(), + &mut app.webviews.borrow_mut(), + None, + "init", + ); window.set_toolbar_height(minibrowser.toolbar_height); } - // Whether or not to recomposite during the next RedrawRequested event. - // Normally this is true, including for RedrawRequested events that come from the platform - // (e.g. X11 without picom or similar) when an offscreen or obscured window becomes visible. - // If we are calling request_redraw in response to the compositor having painted to this - // frame, set this to false, so we can avoid an unnecessary recomposite. - let mut need_recomposite = true; - let t_start = Instant::now(); let mut t = t_start; let ev_waker = events_loop.create_event_loop_waker(); @@ -208,22 +206,17 @@ impl App { // WARNING: do not defer painting or presenting to some later tick of the event // loop or servoshell may become unresponsive! (servo#30312) - if need_recomposite { - trace!("need_recomposite"); - app.servo.as_mut().unwrap().recomposite(); - } if let Some(mut minibrowser) = app.minibrowser() { minibrowser.update( window.winit_window().unwrap(), + &mut app.webviews.borrow_mut(), app.servo.as_ref().unwrap().offscreen_framebuffer_id(), "RedrawRequested", ); minibrowser.paint(window.winit_window().unwrap()); } - app.servo.as_mut().unwrap().present(); - // By default, the next RedrawRequested event will need to recomposite. - need_recomposite = true; + app.servo.as_mut().unwrap().present(); } // Handle the event @@ -303,6 +296,7 @@ impl App { // redraw, doing so would delay the location update by two frames. minibrowser.update( window.winit_window().unwrap(), + webviews, app.servo.as_ref().unwrap().offscreen_framebuffer_id(), "update_location_in_toolbar", ); @@ -314,13 +308,13 @@ impl App { // The window was resized. trace!("PumpResult::Present::Immediate"); - // Resizes are unusual in that we need to repaint synchronously. - // TODO(servo#30049) can we replace this with the simpler Servo::recomposite? - app.servo.as_mut().unwrap().repaint_synchronously(); - + // If we had resized any of the viewports in response to this, we would need to + // call Servo::repaint_synchronously. At the moment we don’t, so there won’t be + // any paint scheduled, and calling it would hang the compositor forever. if let Some(mut minibrowser) = app.minibrowser() { minibrowser.update( window.winit_window().unwrap(), + &mut app.webviews.borrow_mut(), app.servo.as_ref().unwrap().offscreen_framebuffer_id(), "PumpResult::Present::Immediate", ); @@ -339,10 +333,6 @@ impl App { } else { app.servo.as_mut().unwrap().present(); } - - // We don’t need the compositor to paint to this frame during the redraw event. - // TODO(servo#30331) broken on macOS? - // need_recomposite = false; }, Present::None => {}, } diff --git a/ports/servoshell/headed_window.rs b/ports/servoshell/headed_window.rs index 36f8711878a..540bb2a3fce 100644 --- a/ports/servoshell/headed_window.rs +++ b/ports/servoshell/headed_window.rs @@ -8,7 +8,6 @@ use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::rc::Rc; -use euclid::num::Zero; use euclid::{Angle, Length, Point2D, Rotation3D, Scale, Size2D, UnknownUnit, Vector2D, Vector3D}; use log::{debug, info, trace}; use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; @@ -309,8 +308,11 @@ impl WindowPortsMethods for Window { } fn set_inner_size(&self, size: DeviceIntSize) { + let toolbar_height = self.toolbar_height() * self.hidpi_factor(); + let toolbar_height = toolbar_height.get().ceil() as i32; + let total_size = PhysicalSize::new(size.width, size.height + toolbar_height); self.winit_window - .set_inner_size::>(PhysicalSize::new(size.width, size.height)) + .set_inner_size::>(total_size) } fn set_position(&self, point: DeviceIntPoint) { @@ -399,9 +401,7 @@ impl WindowPortsMethods for Window { } }, winit::event::WindowEvent::CursorMoved { position, .. } => { - let toolbar_height = self.toolbar_height.get() * self.hidpi_factor(); - let mut position = winit_position_to_euclid_point(position).to_f32(); - position -= Size2D::from_lengths(Length::zero(), toolbar_height); + let position = winit_position_to_euclid_point(position); self.mouse_pos.set(position.to_i32()); self.event_queue .borrow_mut() @@ -476,7 +476,9 @@ impl WindowPortsMethods for Window { .resize(physical_size.to_i32()) .expect("Failed to resize"); self.inner_size.set(new_size); - self.event_queue.borrow_mut().push(EmbedderEvent::Resize); + self.event_queue + .borrow_mut() + .push(EmbedderEvent::WindowResize); } }, _ => {}, @@ -510,6 +512,10 @@ impl WindowPortsMethods for Window { Some(&self.winit_window) } + fn toolbar_height(&self) -> Length { + self.toolbar_height.get() + } + fn set_toolbar_height(&self, height: Length) { self.toolbar_height.set(height); } @@ -520,13 +526,8 @@ impl WindowMethods for Window { let window_size = winit_size_to_euclid_size(self.winit_window.outer_size()).to_i32(); let window_origin = self.winit_window.outer_position().unwrap_or_default(); let window_origin = winit_position_to_euclid_point(window_origin).to_i32(); - let inner_size = winit_size_to_euclid_size(self.winit_window.inner_size()).to_f32(); - - // Subtract the minibrowser toolbar height if any - let toolbar_height = self.toolbar_height.get() * self.hidpi_factor(); - let viewport_size = inner_size - Size2D::from_lengths(Length::zero(), toolbar_height); - let viewport_origin = DeviceIntPoint::zero(); // bottom left + let viewport_size = winit_size_to_euclid_size(self.winit_window.inner_size()).to_f32(); let viewport = DeviceIntRect::from_origin_and_size(viewport_origin, viewport_size.to_i32()); let screen = self.screen_size.to_i32(); diff --git a/ports/servoshell/headless_window.rs b/ports/servoshell/headless_window.rs index aaa14e3608e..93ff68da0b8 100644 --- a/ports/servoshell/headless_window.rs +++ b/ports/servoshell/headless_window.rs @@ -8,6 +8,7 @@ use std::cell::Cell; use std::rc::Rc; use std::sync::RwLock; +use euclid::num::Zero; use euclid::{Length, Point2D, Rotation3D, Scale, Size2D, UnknownUnit, Vector3D}; use log::warn; use servo::compositing::windowing::{ @@ -93,7 +94,7 @@ impl WindowPortsMethods for Window { Ok(()) => { self.inner_size.set(new_size); if let Ok(ref mut queue) = self.event_queue.write() { - queue.push(EmbedderEvent::Resize); + queue.push(EmbedderEvent::WindowResize); } }, Err(error) => warn!("Could not resize window: {error:?}"), @@ -148,6 +149,10 @@ impl WindowPortsMethods for Window { None } + fn toolbar_height(&self) -> Length { + Length::zero() + } + fn set_toolbar_height(&self, _height: Length) { unimplemented!("headless Window only") } diff --git a/ports/servoshell/minibrowser.rs b/ports/servoshell/minibrowser.rs index 811ac893cc8..08cb7c3364e 100644 --- a/ports/servoshell/minibrowser.rs +++ b/ports/servoshell/minibrowser.rs @@ -7,10 +7,13 @@ use std::num::NonZeroU32; use std::sync::Arc; use std::time::Instant; -use egui::{CentralPanel, Color32, Frame, Key, Modifiers, PaintCallback, Spinner, TopBottomPanel}; +use egui::{ + CentralPanel, Color32, Frame, Key, Modifiers, PaintCallback, Pos2, Spinner, TopBottomPanel, + Vec2, +}; use egui_glow::CallbackFn; use egui_winit::EventResponse; -use euclid::{Length, Point2D, Scale}; +use euclid::{Box2D, Length, Point2D, Scale, Size2D}; use gleam::gl; use glow::NativeFramebuffer; use log::{trace, warn}; @@ -19,6 +22,7 @@ use servo::msg::constellation_msg::TraversalDirection; use servo::rendering_context::RenderingContext; use servo::servo_geometry::DeviceIndependentPixel; use servo::servo_url::ServoUrl; +use servo::style_traits::DevicePixel; use crate::egui_glue::EguiGlow; use crate::events_loop::EventsLoop; @@ -124,6 +128,7 @@ impl Minibrowser { pub fn update( &mut self, window: &winit::window::Window, + webviews: &mut WebViewManager, servo_framebuffer_id: Option, reason: &'static str, ) { @@ -203,9 +208,32 @@ impl Minibrowser { // point, but the Context is correct and the TopBottomPanel is wrong. *toolbar_height = Length::new(ctx.available_rect().min.y); + let scale = + Scale::<_, DeviceIndependentPixel, DevicePixel>::new(ctx.pixels_per_point()); + let Some(focused_webview_id) = webviews.focused_webview_id() else { + return; + }; + let Some(webview) = webviews.get_mut(focused_webview_id) else { + return; + }; + let mut embedder_events = vec![]; CentralPanel::default() .frame(Frame::none()) .show(ctx, |ui| { + let Pos2 { x, y } = ui.cursor().min; + let Vec2 { + x: width, + y: height, + } = ui.available_size(); + let rect = Box2D::from_origin_and_size( + Point2D::new(x, y), + Size2D::new(width, height), + ) * scale; + if rect != webview.rect { + webview.rect = rect; + embedder_events + .push(EmbedderEvent::MoveResizeWebView(focused_webview_id, rect)); + } let min = ui.cursor().min; let size = ui.available_size(); let rect = egui::Rect::from_min_size(min, size); @@ -255,6 +283,10 @@ impl Minibrowser { }); }); + if !embedder_events.is_empty() { + webviews.handle_window_events(embedder_events); + } + *last_update = now; }); } diff --git a/ports/servoshell/tracing.rs b/ports/servoshell/tracing.rs index e74b35633dd..394765d1423 100644 --- a/ports/servoshell/tracing.rs +++ b/ports/servoshell/tracing.rs @@ -81,7 +81,7 @@ mod from_winit { Self::Suspended => target!("Suspended"), Self::Resumed => target!("Resumed"), Self::MainEventsCleared => target!("MainEventsCleared"), - Self::RedrawRequested(_) => target!("RedrawRequested"), + Self::RedrawRequested(..) => target!("RedrawRequested"), Self::RedrawEventsCleared => target!("RedrawEventsCleared"), Self::LoopDestroyed => target!("LoopDestroyed"), } @@ -96,18 +96,18 @@ mod from_winit { }; } match self { - Self::Resized(_) => target_variant!("Resized"), - Self::Moved(_) => target_variant!("Moved"), + Self::Resized(..) => target_variant!("Resized"), + Self::Moved(..) => target_variant!("Moved"), Self::CloseRequested => target_variant!("CloseRequested"), Self::Destroyed => target_variant!("Destroyed"), - Self::DroppedFile(_) => target_variant!("DroppedFile"), - Self::HoveredFile(_) => target_variant!("HoveredFile"), + Self::DroppedFile(..) => target_variant!("DroppedFile"), + Self::HoveredFile(..) => target_variant!("HoveredFile"), Self::HoveredFileCancelled => target_variant!("HoveredFileCancelled"), - Self::ReceivedCharacter(_) => target_variant!("ReceivedCharacter"), - Self::Focused(_) => target_variant!("Focused"), + Self::ReceivedCharacter(..) => target_variant!("ReceivedCharacter"), + Self::Focused(..) => target_variant!("Focused"), Self::KeyboardInput { .. } => target_variant!("KeyboardInput"), - Self::ModifiersChanged(_) => target_variant!("ModifiersChanged"), - Self::Ime(_) => target_variant!("Ime"), + Self::ModifiersChanged(..) => target_variant!("ModifiersChanged"), + Self::Ime(..) => target_variant!("Ime"), Self::CursorMoved { .. } => target_variant!("CursorMoved"), Self::CursorEntered { .. } => target_variant!("CursorEntered"), Self::CursorLeft { .. } => target_variant!("CursorLeft"), @@ -118,10 +118,10 @@ mod from_winit { Self::TouchpadRotate { .. } => target_variant!("TouchpadRotate"), Self::TouchpadPressure { .. } => target_variant!("TouchpadPressure"), Self::AxisMotion { .. } => target_variant!("AxisMotion"), - Self::Touch(_) => target_variant!("Touch"), + Self::Touch(..) => target_variant!("Touch"), Self::ScaleFactorChanged { .. } => target_variant!("ScaleFactorChanged"), - Self::ThemeChanged(_) => target_variant!("ThemeChanged"), - Self::Occluded(_) => target_variant!("Occluded"), + Self::ThemeChanged(..) => target_variant!("ThemeChanged"), + Self::Occluded(..) => target_variant!("Occluded"), } } } @@ -139,41 +139,41 @@ mod from_servo { impl LogTarget for servo::embedder_traits::EmbedderMsg { fn log_target(&self) -> &'static str { match self { - Self::Status(_) => target!("Status"), - Self::ChangePageTitle(_) => target!("ChangePageTitle"), - Self::MoveTo(_) => target!("MoveTo"), - Self::ResizeTo(_) => target!("ResizeTo"), - Self::Prompt(_, _) => target!("Prompt"), - Self::ShowContextMenu(_, _, _) => target!("ShowContextMenu"), - Self::AllowNavigationRequest(_, _) => target!("AllowNavigationRequest"), - Self::AllowOpeningWebView(_) => target!("AllowOpeningWebView"), - Self::WebViewOpened(_) => target!("WebViewOpened"), - Self::WebViewClosed(_) => target!("WebViewClosed"), - Self::WebViewFocused(_) => target!("WebViewFocused"), + Self::Status(..) => target!("Status"), + Self::ChangePageTitle(..) => target!("ChangePageTitle"), + Self::MoveTo(..) => target!("MoveTo"), + Self::ResizeTo(..) => target!("ResizeTo"), + Self::Prompt(..) => target!("Prompt"), + Self::ShowContextMenu(..) => target!("ShowContextMenu"), + Self::AllowNavigationRequest(..) => target!("AllowNavigationRequest"), + Self::AllowOpeningWebView(..) => target!("AllowOpeningWebView"), + Self::WebViewOpened(..) => target!("WebViewOpened"), + Self::WebViewClosed(..) => target!("WebViewClosed"), + Self::WebViewFocused(..) => target!("WebViewFocused"), Self::WebViewBlurred => target!("WebViewBlurred"), - Self::AllowUnload(_) => target!("AllowUnload"), - Self::Keyboard(_) => target!("Keyboard"), - Self::GetClipboardContents(_) => target!("GetClipboardContents"), - Self::SetClipboardContents(_) => target!("SetClipboardContents"), - Self::SetCursor(_) => target!("SetCursor"), - Self::NewFavicon(_) => target!("NewFavicon"), + Self::AllowUnload(..) => target!("AllowUnload"), + Self::Keyboard(..) => target!("Keyboard"), + Self::GetClipboardContents(..) => target!("GetClipboardContents"), + Self::SetClipboardContents(..) => target!("SetClipboardContents"), + Self::SetCursor(..) => target!("SetCursor"), + Self::NewFavicon(..) => target!("NewFavicon"), Self::HeadParsed => target!("HeadParsed"), - Self::HistoryChanged(_, _) => target!("HistoryChanged"), - Self::SetFullscreenState(_) => target!("SetFullscreenState"), + Self::HistoryChanged(..) => target!("HistoryChanged"), + Self::SetFullscreenState(..) => target!("SetFullscreenState"), Self::LoadStart => target!("LoadStart"), Self::LoadComplete => target!("LoadComplete"), - Self::Panic(_, _) => target!("Panic"), - Self::GetSelectedBluetoothDevice(_, _) => target!("GetSelectedBluetoothDevice"), - Self::SelectFiles(_, _, _) => target!("SelectFiles"), - Self::PromptPermission(_, _) => target!("PromptPermission"), - Self::ShowIME(_, _, _, _) => target!("ShowIME"), + Self::Panic(..) => target!("Panic"), + Self::GetSelectedBluetoothDevice(..) => target!("GetSelectedBluetoothDevice"), + Self::SelectFiles(..) => target!("SelectFiles"), + Self::PromptPermission(..) => target!("PromptPermission"), + Self::ShowIME(..) => target!("ShowIME"), Self::HideIME => target!("HideIME"), Self::Shutdown => target!("Shutdown"), - Self::ReportProfile(_) => target!("ReportProfile"), - Self::MediaSessionEvent(_) => target!("MediaSessionEvent"), - Self::OnDevtoolsStarted(_, _) => target!("OnDevtoolsStarted"), - Self::ReadyToPresent => target!("ReadyToPresent"), - Self::EventDelivered(_) => target!("EventDelivered"), + Self::ReportProfile(..) => target!("ReportProfile"), + Self::MediaSessionEvent(..) => target!("MediaSessionEvent"), + Self::OnDevtoolsStarted(..) => target!("OnDevtoolsStarted"), + Self::ReadyToPresent(..) => target!("ReadyToPresent"), + Self::EventDelivered(..) => target!("EventDelivered"), } } } @@ -193,36 +193,41 @@ mod to_servo { match self { Self::Idle => target!("Idle"), Self::Refresh => target!("Refresh"), - Self::Resize => target!("Resize"), - Self::AllowNavigationResponse(_, _) => target!("AllowNavigationResponse"), - Self::LoadUrl(_, _) => target!("LoadUrl"), - Self::MouseWindowEventClass(_) => target!("MouseWindowEventClass"), - Self::MouseWindowMoveEventClass(_) => target!("MouseWindowMoveEventClass"), - Self::Touch(_, _, _) => target!("Touch"), - Self::Wheel(_, _) => target!("Wheel"), - Self::Scroll(_, _, _) => target!("Scroll"), - Self::Zoom(_) => target!("Zoom"), - Self::PinchZoom(_) => target!("PinchZoom"), + Self::WindowResize => target!("WindowResize"), + Self::AllowNavigationResponse(..) => target!("AllowNavigationResponse"), + Self::LoadUrl(..) => target!("LoadUrl"), + Self::MouseWindowEventClass(..) => target!("MouseWindowEventClass"), + Self::MouseWindowMoveEventClass(..) => target!("MouseWindowMoveEventClass"), + Self::Touch(..) => target!("Touch"), + Self::Wheel(..) => target!("Wheel"), + Self::Scroll(..) => target!("Scroll"), + Self::Zoom(..) => target!("Zoom"), + Self::PinchZoom(..) => target!("PinchZoom"), Self::ResetZoom => target!("ResetZoom"), - Self::Navigation(_, _) => target!("Navigation"), + Self::Navigation(..) => target!("Navigation"), Self::Quit => target!("Quit"), - Self::ExitFullScreen(_) => target!("ExitFullScreen"), - Self::Keyboard(_) => target!("Keyboard"), - Self::Reload(_) => target!("Reload"), - Self::NewWebView(_, _) => target!("NewWebView"), - Self::CloseWebView(_) => target!("CloseWebView"), - Self::SendError(_, _) => target!("SendError"), - Self::FocusWebView(_) => target!("FocusWebView"), - Self::ToggleWebRenderDebug(_) => target!("ToggleWebRenderDebug"), + Self::ExitFullScreen(..) => target!("ExitFullScreen"), + Self::Keyboard(..) => target!("Keyboard"), + Self::Reload(..) => target!("Reload"), + Self::NewWebView(..) => target!("NewWebView"), + Self::CloseWebView(..) => target!("CloseWebView"), + Self::SendError(..) => target!("SendError"), + Self::MoveResizeWebView(..) => target!("MoveResizeWebView"), + Self::ShowWebView(..) => target!("ShowWebView"), + Self::HideWebView(..) => target!("HideWebView"), + Self::RaiseWebViewToTop(..) => target!("RaiseWebViewToTop"), + Self::FocusWebView(..) => target!("FocusWebView"), + Self::BlurWebView => target!("BlurWebView"), + Self::ToggleWebRenderDebug(..) => target!("ToggleWebRenderDebug"), Self::CaptureWebRender => target!("CaptureWebRender"), Self::ClearCache => target!("ClearCache"), - Self::ToggleSamplingProfiler(_, _) => target!("ToggleSamplingProfiler"), - Self::MediaSessionAction(_) => target!("MediaSessionAction"), - Self::SetWebViewThrottled(_, _) => target!("SetWebViewThrottled"), + Self::ToggleSamplingProfiler(..) => target!("ToggleSamplingProfiler"), + Self::MediaSessionAction(..) => target!("MediaSessionAction"), + Self::SetWebViewThrottled(..) => target!("SetWebViewThrottled"), Self::IMEDismissed => target!("IMEDismissed"), Self::InvalidateNativeSurface => target!("InvalidateNativeSurface"), - Self::ReplaceNativeSurface(_, _) => target!("ReplaceNativeSurface"), - Self::Gamepad(_) => target!("Gamepad"), + Self::ReplaceNativeSurface(..) => target!("ReplaceNativeSurface"), + Self::Gamepad(..) => target!("Gamepad"), } } } diff --git a/ports/servoshell/webview.rs b/ports/servoshell/webview.rs index 21d3b0aa464..302ad27ba96 100644 --- a/ports/servoshell/webview.rs +++ b/ports/servoshell/webview.rs @@ -26,6 +26,7 @@ use servo::script_traits::{ }; use servo::servo_config::opts; use servo::servo_url::ServoUrl; +use servo::webrender_api::units::DeviceRect; use servo::webrender_api::ScrollLocation; use tinyfiledialogs::{self, MessageBoxIcon, OkCancel, YesNo}; @@ -60,7 +61,9 @@ pub struct WebViewManager { } #[derive(Debug)] -pub struct WebView {} +pub struct WebView { + pub rect: DeviceRect, +} pub struct ServoEventResponse { pub need_present: bool, @@ -111,6 +114,14 @@ where self.focused_webview_id } + pub fn get_mut(&mut self, webview_id: WebViewId) -> Option<&mut WebView> { + self.webviews.get_mut(&webview_id) + } + + pub fn focused_webview_id(&self) -> Option { + self.focused_webview_id.clone() + } + pub fn current_url_string(&self) -> Option<&str> { self.current_url_string.as_deref() } @@ -453,6 +464,20 @@ where self.window.set_position(point); }, EmbedderMsg::ResizeTo(size) => { + if let Some(webview_id) = webview_id { + let new_rect = self.get_mut(webview_id).and_then(|webview| { + if webview.rect.size() != size.to_f32() { + webview.rect.set_size(size.to_f32()); + Some(webview.rect) + } else { + None + } + }); + if let Some(new_rect) = new_rect { + self.event_queue + .push(EmbedderEvent::MoveResizeWebView(webview_id, new_rect)); + } + } self.window.set_inner_size(size); }, EmbedderMsg::Prompt(definition, origin) => { @@ -555,10 +580,22 @@ where }; }, EmbedderMsg::WebViewOpened(new_webview_id) => { - self.webviews.insert(new_webview_id, WebView {}); + let scale = self.window.hidpi_factor().get(); + let toolbar = self.window.toolbar_height().get(); + + // Adjust for our toolbar height. + // TODO: Adjust for egui window decorations if we end up using those + let mut rect = self.window.get_coordinates().get_viewport().to_f32(); + rect.min.y += toolbar * scale; + + self.webviews.insert(new_webview_id, WebView { rect }); self.creation_order.push(new_webview_id); self.event_queue .push(EmbedderEvent::FocusWebView(new_webview_id)); + self.event_queue + .push(EmbedderEvent::MoveResizeWebView(new_webview_id, rect)); + self.event_queue + .push(EmbedderEvent::RaiseWebViewToTop(new_webview_id, true)); }, EmbedderMsg::WebViewClosed(webview_id) => { self.webviews.retain(|&id, _| id != webview_id); @@ -573,6 +610,10 @@ where }, EmbedderMsg::WebViewFocused(webview_id) => { self.focused_webview_id = Some(webview_id); + // Show the most recently created webview and hide all others. + // TODO: Stop doing this once we have full multiple webviews support + self.event_queue + .push(EmbedderEvent::ShowWebView(webview_id, true)); }, EmbedderMsg::WebViewBlurred => { self.focused_webview_id = None; @@ -681,15 +722,18 @@ where EmbedderMsg::ShowContextMenu(sender, ..) => { let _ = sender.send(ContextMenuResult::Ignored); }, - EmbedderMsg::ReadyToPresent => { + EmbedderMsg::ReadyToPresent(_webview_ids) => { need_present = true; }, EmbedderMsg::EventDelivered(event) => { if let (Some(webview_id), CompositorEventVariant::MouseButtonEvent) = (webview_id, event) { - // TODO Focus webview and/or raise to top if needed. trace!("{}: Got a mouse button event", webview_id); + self.event_queue + .push(EmbedderEvent::RaiseWebViewToTop(webview_id, true)); + self.event_queue + .push(EmbedderEvent::FocusWebView(webview_id)); } }, } diff --git a/ports/servoshell/window_trait.rs b/ports/servoshell/window_trait.rs index bc31ef396e3..04d5db60fc8 100644 --- a/ports/servoshell/window_trait.rs +++ b/ports/servoshell/window_trait.rs @@ -47,5 +47,6 @@ pub trait WindowPortsMethods: WindowMethods { events_loop: &winit::event_loop::EventLoopWindowTarget, ) -> Box; fn winit_window(&self) -> Option<&winit::window::Window>; + fn toolbar_height(&self) -> Length; fn set_toolbar_height(&self, height: Length); } diff --git a/python/servo/testing_commands.py b/python/servo/testing_commands.py index bedabde0248..22a3110902a 100644 --- a/python/servo/testing_commands.py +++ b/python/servo/testing_commands.py @@ -162,6 +162,7 @@ class MachCommands(CommandBase): "crown", "constellation", "style_config", + "compositing", ] if not packages: packages = set(os.listdir(path.join(self.context.topdir, "tests", "unit"))) - set(['.DS_Store'])