diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index c7c07951b1a..bbc391ff609 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -2,7 +2,7 @@ * 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::cell::Cell; +use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::env; use std::fs::{File, create_dir_all}; @@ -26,7 +26,7 @@ use embedder_traits::{ ShutdownState, TouchEvent, TouchEventType, TouchId, }; use euclid::{Box2D, Point2D, Rect, Scale, Size2D, Transform3D, Vector2D}; -use fnv::{FnvHashMap, FnvHashSet}; +use fnv::FnvHashMap; use ipc_channel::ipc::{self, IpcSharedMemory}; use libc::c_void; use log::{debug, info, trace, warn}; @@ -92,6 +92,11 @@ pub struct ServoRenderer { /// Our top-level browsing contexts. webviews: WebViewManager, + /// This is a temporary map between [`PipelineId`]s and their associated [`WebViewId`]. Once + /// all renderer operations become per-`WebView` this map can be removed, but we still sometimes + /// need to work backwards to figure out what `WebView` is associated with a `Pipeline`. + pipeline_to_webview_map: Rc>>, + /// Tracks whether we are in the process of shutting down, or have shut down and should close /// the compositor. This is shared with the `Servo` instance. shutdown_state: Rc>, @@ -128,9 +133,6 @@ pub struct IOCompositor { /// The application window. pub window: Rc, - /// Tracks details about each active pipeline that the compositor knows about. - pipeline_details: HashMap, - /// "Mobile-style" zoom that does not reflow the page. viewport_zoom: PinchZoomFactor, @@ -169,13 +171,6 @@ pub struct IOCompositor { /// The surfman instance that webrender targets rendering_context: Rc, - /// A per-pipeline queue of display lists that have not yet been rendered by WebRender. Layout - /// expects WebRender to paint each given epoch. Once the compositor paints a frame with that - /// epoch's display list, it will be removed from the queue and the paint time will be recorded - /// as a metric. In case new display lists come faster than painting a metric might never be - /// recorded. - pending_paint_metrics: HashMap>, - /// The coordinates of the native window, its view and the screen. embedder_coordinates: EmbedderCoordinates, @@ -232,39 +227,86 @@ bitflags! { } } -struct PipelineDetails { +pub(crate) struct PipelineDetails { /// The pipeline associated with this PipelineDetails object. - pipeline: Option, + pub pipeline: Option, + + /// The [`PipelineId`] of this pipeline. + pub id: PipelineId, /// The id of the parent pipeline, if any. - parent_pipeline_id: Option, + pub 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, + pub most_recent_display_list_epoch: Option, /// Whether animations are running - animations_running: bool, + pub animations_running: bool, /// Whether there are animation callbacks - animation_callbacks_running: bool, + pub animation_callbacks_running: bool, /// Whether to use less resources by stopping animations. - throttled: bool, + pub throttled: bool, /// Hit test items for this pipeline. This is used to map WebRender hit test /// information to the full information necessary for Servo. - hit_test_items: Vec, + pub hit_test_items: Vec, /// The compositor-side [ScrollTree]. This is used to allow finding and scrolling /// nodes in the compositor before forwarding new offsets to WebRender. - scroll_tree: ScrollTree, + pub scroll_tree: ScrollTree, + + /// A per-pipeline queue of display lists that have not yet been rendered by WebRender. Layout + /// expects WebRender to paint each given epoch. Once the compositor paints a frame with that + /// epoch's display list, it will be removed from the queue and the paint time will be recorded + /// as a metric. In case new display lists come faster than painting a metric might never be + /// recorded. + pub pending_paint_metrics: Vec, } impl PipelineDetails { - fn new() -> PipelineDetails { + pub(crate) fn animations_or_animation_callbacks_running(&self) -> bool { + self.animations_running || self.animation_callbacks_running + } + + pub(crate) fn animation_callbacks_running(&self) -> bool { + self.animation_callbacks_running + } + + pub(crate) fn tick_animations(&self, compositor: &IOCompositor) -> bool { + let animation_callbacks_running = self.animation_callbacks_running; + let animations_running = self.animations_running; + if !animation_callbacks_running && !animations_running { + return false; + } + + if self.throttled { + return false; + } + + let mut tick_type = AnimationTickType::empty(); + if animations_running { + tick_type.insert(AnimationTickType::CSS_ANIMATIONS_AND_TRANSITIONS); + } + if animation_callbacks_running { + tick_type.insert(AnimationTickType::REQUEST_ANIMATION_FRAME); + } + + let msg = ConstellationMsg::TickAnimation(self.id, tick_type); + if let Err(e) = compositor.global.constellation_sender.send(msg) { + warn!("Sending tick to constellation failed ({:?}).", e); + } + true + } +} + +impl PipelineDetails { + pub(crate) fn new(id: PipelineId) -> PipelineDetails { PipelineDetails { pipeline: None, + id, parent_pipeline_id: None, most_recent_display_list_epoch: None, animations_running: false, @@ -272,6 +314,7 @@ impl PipelineDetails { throttled: false, hit_test_items: Vec::new(), scroll_tree: ScrollTree::default(), + pending_paint_metrics: Vec::new(), } } @@ -310,6 +353,7 @@ impl IOCompositor { global: ServoRenderer { shutdown_state: state.shutdown_state, webviews: WebViewManager::default(), + pipeline_to_webview_map: Default::default(), compositor_receiver: state.receiver, constellation_sender: state.constellation_chan, time_profiler_chan: state.time_profiler_chan, @@ -321,7 +365,6 @@ impl IOCompositor { }, embedder_coordinates: window.get_coordinates(), window, - pipeline_details: HashMap::new(), needs_repaint: Cell::default(), touch_handler: TouchHandler::new(), pending_scroll_zoom_events: Vec::new(), @@ -335,7 +378,6 @@ impl IOCompositor { webrender: Some(state.webrender), webrender_document: state.webrender_document, rendering_context: state.rendering_context, - pending_paint_metrics: HashMap::new(), cursor: Cursor::None, cursor_pos: DevicePoint::new(0.0, 0.0), convert_mouse_to_touch, @@ -378,22 +420,24 @@ impl IOCompositor { Some(cursor) if cursor != self.cursor => cursor, _ => return, }; + let Some(webview_id) = self - .pipeline_details(result.pipeline_id) - .pipeline - .as_ref() - .map(|composition_pipeline| composition_pipeline.top_level_browsing_context_id) + .global + .pipeline_to_webview_map + .borrow() + .get(&result.pipeline_id) + .cloned() else { - warn!( - "Updating cursor for not-yet-rendered pipeline: {}", - result.pipeline_id - ); + warn!("Couldn't update cursor for non-WebView-associated pipeline"); return; }; self.cursor = cursor; - let msg = ConstellationMsg::SetCursor(webview_id, cursor); - if let Err(e) = self.global.constellation_sender.send(msg) { + if let Err(e) = self + .global + .constellation_sender + .send(ConstellationMsg::SetCursor(webview_id, cursor)) + { warn!("Sending event to constellation failed ({:?}).", e); } } @@ -433,13 +477,34 @@ impl IOCompositor { } match msg { - CompositorMsg::ChangeRunningAnimationsState(pipeline_id, animation_state) => { - self.change_running_animations_state(pipeline_id, animation_state); + CompositorMsg::ChangeRunningAnimationsState( + webview_id, + pipeline_id, + animation_state, + ) => { + let mut throttled = true; + if let Some(webview) = self.global.webviews.get_mut(webview_id) { + throttled = + webview.change_running_animations_state(pipeline_id, animation_state); + } + + // These operations should eventually happen per-WebView, but they are global now as rendering + // is still global to all WebViews. + if !throttled && animation_state == AnimationState::AnimationsPresent { + self.set_needs_repaint(RepaintReason::ChangedAnimationState); + } + + if !throttled && animation_state == AnimationState::AnimationCallbacksPresent { + // We need to fetch the WebView again in order to avoid a double borrow. + if let Some(webview) = self.global.webviews.get(webview_id) { + webview.tick_animations_for_pipeline(pipeline_id, self); + } + } }, CompositorMsg::CreateOrUpdateWebView(frame_tree) => { self.set_frame_tree_for_webview(&frame_tree); - self.send_scroll_positions_to_layout_for_pipeline(&frame_tree.pipeline.id); + self.send_scroll_positions_to_layout_for_pipeline(frame_tree.pipeline.id); }, CompositorMsg::RemoveWebView(top_level_browsing_context_id) => { @@ -474,14 +539,21 @@ impl IOCompositor { self.set_needs_repaint(RepaintReason::ReadyForScreenshot); }, - CompositorMsg::SetThrottled(pipeline_id, throttled) => { - self.pipeline_details(pipeline_id).throttled = throttled; - self.process_animations(true); + CompositorMsg::SetThrottled(webview_id, pipeline_id, throttled) => { + if let Some(webview) = self.global.webviews.get_mut(webview_id) { + webview.set_throttled(pipeline_id, throttled); + self.process_animations(true); + } }, - CompositorMsg::PipelineExited(pipeline_id, sender) => { - debug!("Compositor got pipeline exited: {:?}", pipeline_id); - self.remove_pipeline_root_layer(pipeline_id); + CompositorMsg::PipelineExited(webview_id, pipeline_id, sender) => { + debug!( + "Compositor got pipeline exited: {:?} {:?}", + webview_id, pipeline_id + ); + if let Some(webview) = self.global.webviews.get_mut(webview_id) { + webview.remove_pipeline(pipeline_id); + } let _ = sender.send(()); }, @@ -494,7 +566,7 @@ impl IOCompositor { } } - if recomposite_needed || self.animation_callbacks_active() { + if recomposite_needed || self.animation_callbacks_running() { self.set_needs_repaint(RepaintReason::NewWebRenderFrame); } }, @@ -521,11 +593,10 @@ impl IOCompositor { self.dispatch_input_event(InputEvent::MouseMove(MouseMoveEvent { point })); }, - CompositorMsg::PendingPaintMetric(pipeline_id, epoch) => { - self.pending_paint_metrics - .entry(pipeline_id) - .or_default() - .push(epoch); + CompositorMsg::PendingPaintMetric(webview_id, pipeline_id, epoch) => { + if let Some(webview) = self.global.webviews.get_mut(webview_id) { + webview.add_pending_paint_metric(pipeline_id, epoch); + } }, CompositorMsg::CrossProcess(cross_proces_message) => { @@ -552,14 +623,18 @@ impl IOCompositor { }, CrossProcessCompositorMessage::SendScrollNode( + webview_id, pipeline_id, point, external_scroll_id, ) => { + let Some(webview) = self.global.webviews.get_mut(webview_id) else { + return; + }; + let pipeline_id = pipeline_id.into(); - let pipeline_details = match self.pipeline_details.get_mut(&pipeline_id) { - Some(details) => details, - None => return, + let Some(pipeline_details) = webview.pipelines.get_mut(&pipeline_id) else { + return; }; let offset = LayoutVector2D::new(point.x, point.y); @@ -589,6 +664,7 @@ impl IOCompositor { }, CrossProcessCompositorMessage::SendDisplayList { + webview_id, display_list_info, display_list_descriptor, display_list_receiver, @@ -633,8 +709,13 @@ impl IOCompositor { servo_profiling = true, ) .entered(); + + let Some(webview) = self.global.webviews.get_mut(webview_id) else { + return warn!("Could not find WebView for incoming display list"); + }; + let pipeline_id = display_list_info.pipeline_id; - let details = self.pipeline_details(pipeline_id.into()); + let details = webview.pipeline_details(pipeline_id.into()); details.most_recent_display_list_epoch = Some(display_list_info.epoch); details.hit_test_items = display_list_info.hit_test_info; details.install_new_scroll_tree(display_list_info.scroll_tree); @@ -777,9 +858,14 @@ impl IOCompositor { /// compositor no longer does any WebRender frame generation. fn handle_browser_message_while_shutting_down(&mut self, msg: CompositorMsg) { match msg { - CompositorMsg::PipelineExited(pipeline_id, sender) => { - debug!("Compositor got pipeline exited: {:?}", pipeline_id); - self.remove_pipeline_root_layer(pipeline_id); + CompositorMsg::PipelineExited(webview_id, pipeline_id, sender) => { + debug!( + "Compositor got pipeline exited: {:?} {:?}", + webview_id, pipeline_id + ); + if let Some(webview) = self.global.webviews.get_mut(webview_id) { + webview.remove_pipeline(pipeline_id); + } let _ = sender.send(()); }, CompositorMsg::CrossProcess(CrossProcessCompositorMessage::GenerateImageKey( @@ -838,61 +924,6 @@ impl IOCompositor { transaction.generate_frame(0, true /* present */, reason); } - /// Sets or unsets the animations-running flag for the given pipeline, and schedules a - /// recomposite if necessary. - fn change_running_animations_state( - &mut self, - pipeline_id: PipelineId, - animation_state: AnimationState, - ) { - match animation_state { - AnimationState::AnimationsPresent => { - let throttled = self.pipeline_details(pipeline_id).throttled; - self.pipeline_details(pipeline_id).animations_running = true; - if !throttled { - self.set_needs_repaint(RepaintReason::ChangedAnimationState); - } - }, - AnimationState::AnimationCallbacksPresent => { - let throttled = self.pipeline_details(pipeline_id).throttled; - self.pipeline_details(pipeline_id) - .animation_callbacks_running = true; - if !throttled { - self.tick_animations_for_pipeline(pipeline_id); - } - }, - AnimationState::NoAnimationsPresent => { - self.pipeline_details(pipeline_id).animations_running = false; - }, - AnimationState::NoAnimationCallbacksPresent => { - self.pipeline_details(pipeline_id) - .animation_callbacks_running = false; - }, - } - } - - fn pipeline_details(&mut self, pipeline_id: PipelineId) -> &mut PipelineDetails { - self.pipeline_details - .entry(pipeline_id) - .or_insert_with(PipelineDetails::new); - self.pipeline_details - .get_mut(&pipeline_id) - .expect("Insert then get failed!") - } - - pub fn pipeline(&self, pipeline_id: PipelineId) -> Option<&CompositionPipeline> { - match self.pipeline_details.get(&pipeline_id) { - Some(details) => details.pipeline.as_ref(), - None => { - warn!( - "Compositor layer has an unknown pipeline ({:?}).", - pipeline_id - ); - None - }, - } - } - /// 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. @@ -942,7 +973,7 @@ impl IOCompositor { 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]); for (_, webview) in self.global.webviews.painting_order() { - if let Some(pipeline_id) = webview.pipeline_id { + if let Some(pipeline_id) = webview.root_pipeline_id { let scaled_webview_rect = webview.rect / zoom_factor; builder.push_iframe( LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()), @@ -975,20 +1006,23 @@ impl IOCompositor { /// TODO(mrobinson): Could we only send offsets for the branch being modified /// and not the entire scene? fn update_transaction_with_all_scroll_offsets(&self, transaction: &mut Transaction) { - for details in self.pipeline_details.values() { - for node in details.scroll_tree.nodes.iter() { - let (Some(offset), Some(external_id)) = (node.offset(), node.external_id()) else { - continue; - }; + for webview in self.global.webviews.iter() { + for details in webview.pipelines.values() { + for node in details.scroll_tree.nodes.iter() { + let (Some(offset), Some(external_id)) = (node.offset(), node.external_id()) + else { + continue; + }; - let offset = LayoutVector2D::new(-offset.x, -offset.y); - transaction.set_scroll_offsets( - external_id, - vec![SampledScrollOffset { - offset, - generation: 0, - }], - ); + let offset = LayoutVector2D::new(-offset.x, -offset.y); + transaction.set_scroll_offsets( + external_id, + vec![SampledScrollOffset { + offset, + generation: 0, + }], + ); + } } } } @@ -996,8 +1030,11 @@ impl IOCompositor { pub fn add_webview(&mut self, webview_id: WebViewId) { let size = self.rendering_context.size2d().to_f32(); self.global.webviews.entry(webview_id).or_insert(WebView { - pipeline_id: None, + id: webview_id, + root_pipeline_id: None, rect: Box2D::from_origin_and_size(Point2D::origin(), size), + pipelines: Default::default(), + pipeline_to_webview_map: self.global.pipeline_to_webview_map.clone(), }); } @@ -1012,31 +1049,18 @@ impl IOCompositor { return; }; - let new_pipeline_id = Some(frame_tree.pipeline.id); - if new_pipeline_id != webview.pipeline_id { - debug!( - "{webview_id:?}: Updating webview from pipeline {:?} to {new_pipeline_id:?}", - webview.pipeline_id - ); - } - webview.pipeline_id = new_pipeline_id; - + webview.set_frame_tree(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); } - fn remove_webview(&mut self, top_level_browsing_context_id: TopLevelBrowsingContextId) { - debug!("{}: Removing", top_level_browsing_context_id); - let Ok(webview) = self.global.webviews.remove(top_level_browsing_context_id) else { - warn!("{top_level_browsing_context_id}: Removing unknown webview"); + fn remove_webview(&mut self, webview_id: WebViewId) { + debug!("{}: Removing", webview_id); + if self.global.webviews.remove(webview_id).is_err() { + warn!("{webview_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); - } } pub fn move_resize_webview(&mut self, webview_id: TopLevelBrowsingContextId, rect: DeviceRect) { @@ -1144,69 +1168,6 @@ impl IOCompositor { } } - 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 - // navigating backward and forward. - fn collect_pipelines( - pipelines: &mut FnvHashSet, - frame_tree: &SendableFrameTree, - ) { - pipelines.insert(frame_tree.pipeline.id); - for kid in &frame_tree.children { - collect_pipelines(pipelines, kid); - } - } - - let mut attached_pipelines: FnvHashSet = FnvHashSet::default(); - collect_pipelines(&mut attached_pipelines, frame_tree); - - self.pipeline_details - .iter_mut() - .filter(|(id, _)| !attached_pipelines.contains(id)) - .for_each(|(_, details)| { - details.scroll_tree.nodes.iter_mut().for_each(|node| { - node.set_offset(LayoutVector2D::zero()); - }) - }) - } - - 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_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); - } - } - - fn remove_pipeline_root_layer(&mut self, pipeline_id: PipelineId) { - self.pipeline_details.remove(&pipeline_id); - } - pub fn on_embedder_window_moved(&mut self) { self.embedder_coordinates = self.window.get_coordinates(); } @@ -1333,10 +1294,7 @@ impl IOCompositor { .iter() .filter_map(|item| { let pipeline_id = item.pipeline.into(); - let details = match self.pipeline_details.get(&pipeline_id) { - Some(details) => details, - None => return None, - }; + let details = self.details_for_pipeline(pipeline_id)?; // If the epoch in the tag does not match the current epoch of the pipeline, // then the hit test is against an old version of the display list and we @@ -1760,7 +1718,7 @@ impl IOCompositor { generation: 0, }], ); - self.send_scroll_positions_to_layout_for_pipeline(&pipeline_id); + self.send_scroll_positions_to_layout_for_pipeline(pipeline_id); } self.generate_frame(&mut transaction, RenderReasons::APZ); @@ -1804,10 +1762,9 @@ impl IOCompositor { .. } in hit_test_results.iter() { + let pipeline_details = self.details_for_pipeline_mut(*pipeline_id)?; if previous_pipeline_id.replace(pipeline_id) != Some(pipeline_id) { - let scroll_result = self - .pipeline_details - .get_mut(pipeline_id)? + let scroll_result = pipeline_details .scroll_tree .scroll_node_or_ancestor(scroll_tree_node, scroll_location); if let Some((external_id, offset)) = scroll_result { @@ -1831,50 +1788,24 @@ impl IOCompositor { } self.last_animation_tick = Instant::now(); - let mut pipeline_ids = vec![]; - for (pipeline_id, pipeline_details) in &self.pipeline_details { - if (pipeline_details.animations_running || pipeline_details.animation_callbacks_running) && - !pipeline_details.throttled - { - pipeline_ids.push(*pipeline_id); - } - } #[cfg(feature = "webxr")] let webxr_running = self.global.webxr_main_thread.running(); #[cfg(not(feature = "webxr"))] let webxr_running = false; - let animation_state = if pipeline_ids.is_empty() && !webxr_running { + + let any_webviews_animating = !self + .global + .webviews + .iter() + .all(|webview| !webview.tick_all_animations(self)); + + let animation_state = if !any_webviews_animating && !webxr_running { windowing::AnimationState::Idle } else { windowing::AnimationState::Animating }; + self.window.set_animation_state(animation_state); - for pipeline_id in &pipeline_ids { - self.tick_animations_for_pipeline(*pipeline_id) - } - } - - fn tick_animations_for_pipeline(&mut self, pipeline_id: PipelineId) { - let animation_callbacks_running = self - .pipeline_details(pipeline_id) - .animation_callbacks_running; - let animations_running = self.pipeline_details(pipeline_id).animations_running; - if !animation_callbacks_running && !animations_running { - return; - } - - let mut tick_type = AnimationTickType::empty(); - if animations_running { - tick_type.insert(AnimationTickType::CSS_ANIMATIONS_AND_TRANSITIONS); - } - if animation_callbacks_running { - tick_type.insert(AnimationTickType::REQUEST_ANIMATION_FRAME); - } - - let msg = ConstellationMsg::TickAnimation(pipeline_id, tick_type); - if let Err(e) = self.global.constellation_sender.send(msg) { - warn!("Sending tick to constellation failed ({:?}).", e); - } } fn hidpi_factor(&self) -> Scale { @@ -1943,10 +1874,40 @@ impl IOCompositor { } } - fn send_scroll_positions_to_layout_for_pipeline(&self, pipeline_id: &PipelineId) { - let details = match self.pipeline_details.get(pipeline_id) { - Some(details) => details, - None => return, + fn details_for_pipeline(&self, pipeline_id: PipelineId) -> Option<&PipelineDetails> { + let webview_id = self + .global + .pipeline_to_webview_map + .borrow() + .get(&pipeline_id) + .cloned()?; + self.global + .webviews + .get(webview_id)? + .pipelines + .get(&pipeline_id) + } + + fn details_for_pipeline_mut( + &mut self, + pipeline_id: PipelineId, + ) -> Option<&mut PipelineDetails> { + let webview_id = self + .global + .pipeline_to_webview_map + .borrow() + .get(&pipeline_id) + .cloned()?; + self.global + .webviews + .get_mut(webview_id)? + .pipelines + .get_mut(&pipeline_id) + } + + fn send_scroll_positions_to_layout_for_pipeline(&self, pipeline_id: PipelineId) { + let Some(details) = self.details_for_pipeline(pipeline_id) else { + return; }; let mut scroll_states = Vec::new(); @@ -1960,32 +1921,25 @@ impl IOCompositor { }); if let Some(pipeline) = details.pipeline.as_ref() { - let message = ScriptThreadMessage::SetScrollStates(*pipeline_id, scroll_states); + let message = ScriptThreadMessage::SetScrollStates(pipeline_id, scroll_states); let _ = pipeline.script_chan.send(message); } } // Check if any pipelines currently have active animations or animation callbacks. - fn animations_active(&self) -> bool { - for details in self.pipeline_details.values() { - // If animations are currently running, then don't bother checking - // with the constellation if the output image is stable. - if details.animations_running { - return true; - } - if details.animation_callbacks_running { - return true; - } - } - - false + fn animations_or_animation_callbacks_running(&self) -> bool { + self.global + .webviews + .iter() + .any(WebView::animations_or_animation_callbacks_running) } /// Returns true if any animation callbacks (ie `requestAnimationFrame`) are waiting for a response. - fn animation_callbacks_active(&self) -> bool { - self.pipeline_details - .values() - .any(|details| details.animation_callbacks_running) + fn animation_callbacks_running(&self) -> bool { + self.global + .webviews + .iter() + .any(WebView::animation_callbacks_running) } /// Query the constellation to see if the current compositor @@ -2001,7 +1955,7 @@ impl IOCompositor { // This gets sent to the constellation for comparison with the current // frame tree. let mut pipeline_epochs = HashMap::new(); - for id in self.pipeline_details.keys() { + for id in self.global.webviews.iter().flat_map(WebView::pipeline_ids) { if let Some(WebRenderEpoch(epoch)) = self .webrender .as_ref() @@ -2111,7 +2065,7 @@ impl IOCompositor { // The current image may be ready to output. However, if there are animations active, // tick those instead and continue waiting for the image output to be stable AND // all active animations to complete. - if self.animations_active() { + if self.animations_or_animation_callbacks_running() { self.process_animations(false); return Err(UnableToComposite::NotReadyToPaintImage( NotReadyToPaint::AnimationsActive, @@ -2153,62 +2107,51 @@ impl IOCompositor { /// current time, inform the constellation about it and remove the pending metric from /// the list. fn send_pending_paint_metrics_messages_after_composite(&mut self) { - if self.pending_paint_metrics.is_empty() { - return; - } - let paint_time = CrossProcessInstant::now(); - let mut pipelines_to_remove = Vec::new(); - let pending_paint_metrics = &mut self.pending_paint_metrics; + for webview_details in self.global.webviews.iter_mut() { + // For each pipeline, determine the current epoch and update paint timing if necessary. + for (pipeline_id, pipeline) in webview_details.pipelines.iter_mut() { + if pipeline.pending_paint_metrics.is_empty() { + continue; + } + let Some(composition_pipeline) = pipeline.pipeline.as_ref() else { + continue; + }; - // For each pending paint metrics pipeline id, determine the current - // epoch and update paint timing if necessary. - for (pipeline_id, pending_epochs) in pending_paint_metrics.iter_mut() { - let Some(WebRenderEpoch(current_epoch)) = self - .webrender - .as_ref() - .and_then(|wr| wr.current_epoch(self.webrender_document, pipeline_id.into())) - else { - continue; - }; + let Some(WebRenderEpoch(current_epoch)) = self + .webrender + .as_ref() + .and_then(|wr| wr.current_epoch(self.webrender_document, pipeline_id.into())) + else { + continue; + }; - // If the pipeline is unknown, stop trying to send paint metrics for it. - let Some(pipeline) = self - .pipeline_details - .get(pipeline_id) - .and_then(|pipeline_details| pipeline_details.pipeline.as_ref()) - else { - pipelines_to_remove.push(*pipeline_id); - continue; - }; + let current_epoch = Epoch(current_epoch); + let Some(index) = pipeline + .pending_paint_metrics + .iter() + .position(|epoch| *epoch == current_epoch) + else { + continue; + }; - let current_epoch = Epoch(current_epoch); - let Some(index) = pending_epochs - .iter() - .position(|epoch| *epoch == current_epoch) - else { - continue; - }; + // Remove all epochs that were pending before the current epochs. They were not and will not, + // be painted. + pipeline.pending_paint_metrics.drain(0..index); - // Remove all epochs that were pending before the current epochs. They were not and will not, - // be painted. - pending_epochs.drain(0..index); - - if let Err(error) = pipeline - .script_chan - .send(ScriptThreadMessage::SetEpochPaintTime( - *pipeline_id, - current_epoch, - paint_time, - )) - { - warn!("Sending RequestLayoutPaintMetric message to layout failed ({error:?})."); + if let Err(error) = + composition_pipeline + .script_chan + .send(ScriptThreadMessage::SetEpochPaintTime( + *pipeline_id, + current_epoch, + paint_time, + )) + { + warn!("Sending RequestLayoutPaintMetric message to layout failed ({error:?})."); + } } } - - for pipeline_id in pipelines_to_remove.iter() { - self.pending_paint_metrics.remove(pipeline_id); - } } fn clear_background(&self) { diff --git a/components/compositing/webview.rs b/components/compositing/webview.rs index fba469539d5..d22419633e8 100644 --- a/components/compositing/webview.rs +++ b/components/compositing/webview.rs @@ -2,19 +2,190 @@ * 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::cell::RefCell; use std::collections::HashMap; -use std::collections::hash_map::Entry; +use std::collections::hash_map::{Entry, Keys, Values, ValuesMut}; +use std::rc::Rc; use base::id::{PipelineId, WebViewId}; -use webrender_api::units::DeviceRect; +use compositing_traits::SendableFrameTree; +use fnv::FnvHashSet; +use log::debug; +use script_traits::AnimationState; +use webrender_api::units::{DeviceRect, LayoutVector2D}; -#[derive(Debug, Default)] -pub struct WebView { - pub pipeline_id: Option, +use crate::IOCompositor; +use crate::compositor::PipelineDetails; + +pub(crate) struct WebView { + /// The [`WebViewId`] of the `WebView` associated with this [`WebViewDetails`]. + pub id: WebViewId, + /// The root [`PipelineId`] of the currently displayed page in this WebView. + pub root_pipeline_id: Option, pub rect: DeviceRect, + /// Tracks details about each active pipeline that the compositor knows about. + pub pipelines: HashMap, + /// This is a temporary map between [`PipelineId`]s and their associated [`WebViewId`]. Once + /// all renderer operations become per-`WebView` this map can be removed, but we still sometimes + /// need to work backwards to figure out what `WebView` is associated with a `Pipeline`. + pub pipeline_to_webview_map: Rc>>, } -#[derive(Debug, Default)] +impl Drop for WebView { + fn drop(&mut self) { + self.pipeline_to_webview_map + .borrow_mut() + .retain(|_, webview_id| self.id != *webview_id); + } +} + +impl WebView { + pub(crate) fn animations_or_animation_callbacks_running(&self) -> bool { + self.pipelines + .values() + .any(PipelineDetails::animations_or_animation_callbacks_running) + } + + pub(crate) fn animation_callbacks_running(&self) -> bool { + self.pipelines + .values() + .any(PipelineDetails::animation_callbacks_running) + } + + pub(crate) fn pipeline_ids(&self) -> Keys<'_, PipelineId, PipelineDetails> { + self.pipelines.keys() + } + + pub(crate) fn pipeline_details(&mut self, pipeline_id: PipelineId) -> &mut PipelineDetails { + self.pipelines.entry(pipeline_id).or_insert_with(|| { + self.pipeline_to_webview_map + .borrow_mut() + .insert(pipeline_id, self.id); + PipelineDetails::new(pipeline_id) + }) + } + + pub(crate) fn set_throttled(&mut self, pipeline_id: PipelineId, throttled: bool) { + self.pipeline_details(pipeline_id).throttled = throttled; + } + + pub(crate) fn remove_pipeline(&mut self, pipeline_id: PipelineId) { + self.pipeline_to_webview_map + .borrow_mut() + .remove(&pipeline_id); + self.pipelines.remove(&pipeline_id); + } + + pub(crate) fn set_frame_tree(&mut self, frame_tree: &SendableFrameTree) { + let pipeline_id = frame_tree.pipeline.id; + let old_pipeline_id = std::mem::replace(&mut self.root_pipeline_id, Some(pipeline_id)); + + if old_pipeline_id != self.root_pipeline_id { + debug!( + "Updating webview ({:?}) from pipeline {:?} to {:?}", + 3, old_pipeline_id, self.root_pipeline_id + ); + } + + self.set_frame_tree_on_pipeline_details(frame_tree, None); + self.reset_scroll_tree_for_unattached_pipelines(frame_tree); + } + + pub(crate) fn set_frame_tree_on_pipeline_details( + &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.set_frame_tree_on_pipeline_details(kid, Some(pipeline_id)); + } + } + + pub(crate) 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 + // navigating backward and forward. + fn collect_pipelines( + pipelines: &mut FnvHashSet, + frame_tree: &SendableFrameTree, + ) { + pipelines.insert(frame_tree.pipeline.id); + for kid in &frame_tree.children { + collect_pipelines(pipelines, kid); + } + } + + let mut attached_pipelines: FnvHashSet = FnvHashSet::default(); + collect_pipelines(&mut attached_pipelines, frame_tree); + + self.pipelines + .iter_mut() + .filter(|(id, _)| !attached_pipelines.contains(id)) + .for_each(|(_, details)| { + details.scroll_tree.nodes.iter_mut().for_each(|node| { + node.set_offset(LayoutVector2D::zero()); + }) + }) + } + + /// Sets or unsets the animations-running flag for the given pipeline, and schedules a + /// recomposite if necessary. Returns true if the pipeline is throttled. + pub(crate) fn change_running_animations_state( + &mut self, + pipeline_id: PipelineId, + animation_state: AnimationState, + ) -> bool { + let pipeline_details = self.pipeline_details(pipeline_id); + match animation_state { + AnimationState::AnimationsPresent => { + pipeline_details.animations_running = true; + }, + AnimationState::AnimationCallbacksPresent => { + pipeline_details.animation_callbacks_running = true; + }, + AnimationState::NoAnimationsPresent => { + pipeline_details.animations_running = false; + }, + AnimationState::NoAnimationCallbacksPresent => { + pipeline_details.animation_callbacks_running = false; + }, + } + pipeline_details.throttled + } + + pub(crate) fn tick_all_animations(&self, compositor: &IOCompositor) -> bool { + let mut ticked_any = false; + for pipeline_details in self.pipelines.values() { + ticked_any = pipeline_details.tick_animations(compositor) || ticked_any; + } + ticked_any + } + + pub(crate) fn tick_animations_for_pipeline( + &self, + pipeline_id: PipelineId, + compositor: &IOCompositor, + ) { + if let Some(pipeline_details) = self.pipelines.get(&pipeline_id) { + pipeline_details.tick_animations(compositor); + } + } + + pub(crate) fn add_pending_paint_metric(&mut self, pipeline_id: PipelineId, epoch: base::Epoch) { + self.pipeline_details(pipeline_id) + .pending_paint_metrics + .push(epoch); + } +} +#[derive(Debug)] 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. @@ -27,6 +198,15 @@ pub struct WebViewManager { #[derive(Clone, Copy, Debug, PartialEq)] pub struct UnknownWebView(pub WebViewId); +impl Default for WebViewManager { + fn default() -> Self { + Self { + webviews: Default::default(), + painting_order: Default::default(), + } + } +} + impl WebViewManager { pub fn remove(&mut self, webview_id: WebViewId) -> Result { self.painting_order.retain(|b| *b != webview_id); @@ -98,6 +278,14 @@ impl WebViewManager { pub fn entry(&mut self, webview_id: WebViewId) -> Entry<'_, WebViewId, WebView> { self.webviews.entry(webview_id) } + + pub fn iter(&self) -> Values<'_, WebViewId, WebView> { + self.webviews.values() + } + + pub fn iter_mut(&mut self) -> ValuesMut<'_, WebViewId, WebView> { + self.webviews.values_mut() + } } #[cfg(test)] diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index c1389e09e56..7f8ca662dfe 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -1446,7 +1446,7 @@ where let (source_pipeline_id, content) = message; trace_script_msg!(content, "{source_pipeline_id}: {content:?}"); - let source_top_ctx_id = match self + let webview_id = match self .pipelines .get(&source_pipeline_id) .map(|pipeline| pipeline.top_level_browsing_context_id) @@ -1521,10 +1521,10 @@ where self.handle_pipeline_exited(source_pipeline_id); }, FromScriptMsg::DiscardDocument => { - self.handle_discard_document(source_top_ctx_id, source_pipeline_id); + self.handle_discard_document(webview_id, source_pipeline_id); }, FromScriptMsg::DiscardTopLevelBrowsingContext => { - self.handle_close_top_level_browsing_context(source_top_ctx_id); + self.handle_close_top_level_browsing_context(webview_id); }, FromScriptMsg::ScriptLoadedURLInIFrame(load_info) => { self.handle_script_loaded_url_in_iframe_msg(load_info); @@ -1541,7 +1541,7 @@ where // Ask the embedder for permission to load a new page. FromScriptMsg::LoadUrl(load_data, history_handling) => { self.schedule_navigation( - source_top_ctx_id, + webview_id, source_pipeline_id, load_data, history_handling, @@ -1552,7 +1552,7 @@ where }, // A page loaded has completed all parsing, script, and reflow messages have been sent. FromScriptMsg::LoadComplete => { - self.handle_load_complete_msg(source_top_ctx_id, source_pipeline_id) + self.handle_load_complete_msg(webview_id, source_pipeline_id) }, // Handle navigating to a fragment FromScriptMsg::NavigatedToFragment(new_url, replacement_enabled) => { @@ -1560,7 +1560,7 @@ where }, // Handle a forward or back request FromScriptMsg::TraverseHistory(direction) => { - self.handle_traverse_history_msg(source_top_ctx_id, direction); + self.handle_traverse_history_msg(webview_id, direction); }, // Handle a push history state request. FromScriptMsg::PushHistoryState(history_state_id, url) => { @@ -1571,7 +1571,7 @@ where }, // Handle a joint session history length request. FromScriptMsg::JointSessionHistoryLength(response_sender) => { - self.handle_joint_session_history_length(source_top_ctx_id, response_sender); + self.handle_joint_session_history_length(webview_id, response_sender); }, // Notification that the new document is ready to become active FromScriptMsg::ActivateDocument => { @@ -1627,7 +1627,7 @@ where response_sender.send(true).unwrap_or_default(); }, FromScriptMsg::LogEntry(thread_name, entry) => { - self.handle_log_entry(Some(source_top_ctx_id), thread_name, entry); + self.handle_log_entry(Some(webview_id), thread_name, entry); }, FromScriptMsg::TouchEventProcessed(result) => self .compositor_proxy @@ -1714,19 +1714,19 @@ where } self.active_media_session = Some(pipeline_id); self.embedder_proxy - .send(EmbedderMsg::MediaSessionEvent(source_top_ctx_id, event)); + .send(EmbedderMsg::MediaSessionEvent(webview_id, event)); }, #[cfg(feature = "webgpu")] FromScriptMsg::RequestAdapter(response_sender, options, ids) => self .handle_wgpu_request( source_pipeline_id, - BrowsingContextId::from(source_top_ctx_id), + BrowsingContextId::from(webview_id), FromScriptMsg::RequestAdapter(response_sender, options, ids), ), #[cfg(feature = "webgpu")] FromScriptMsg::GetWebGPUChan(response_sender) => self.handle_wgpu_request( source_pipeline_id, - BrowsingContextId::from(source_top_ctx_id), + BrowsingContextId::from(webview_id), FromScriptMsg::GetWebGPUChan(response_sender), ), FromScriptMsg::TitleChanged(pipeline, title) => { @@ -2008,8 +2008,8 @@ where fn handle_request_from_layout(&mut self, message: FromLayoutMsg) { trace_layout_msg!(message, "{message:?}"); match message { - FromLayoutMsg::PendingPaintMetric(pipeline_id, epoch) => { - self.handle_pending_paint_metric(pipeline_id, epoch); + FromLayoutMsg::PendingPaintMetric(webview_id, pipeline_id, epoch) => { + self.handle_pending_paint_metric(webview_id, pipeline_id, epoch); }, } } @@ -3440,9 +3440,18 @@ where feature = "tracing", tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") )] - fn handle_pending_paint_metric(&self, pipeline_id: PipelineId, epoch: Epoch) { + fn handle_pending_paint_metric( + &self, + webview_id: WebViewId, + pipeline_id: PipelineId, + epoch: Epoch, + ) { self.compositor_proxy - .send(CompositorMsg::PendingPaintMetric(pipeline_id, epoch)) + .send(CompositorMsg::PendingPaintMetric( + webview_id, + pipeline_id, + epoch, + )) } #[cfg_attr( @@ -3468,6 +3477,7 @@ where pipeline.animation_state = animation_state; self.compositor_proxy .send(CompositorMsg::ChangeRunningAnimationsState( + pipeline.top_level_browsing_context_id, pipeline_id, animation_state, )) diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs index 9b0360a68ec..05c7829e49e 100644 --- a/components/constellation/pipeline.rs +++ b/components/constellation/pipeline.rs @@ -386,8 +386,11 @@ impl Pipeline { // It's OK for the constellation to block on the compositor, // since the compositor never blocks on the constellation. if let Ok((sender, receiver)) = ipc::channel() { - self.compositor_proxy - .send(CompositorMsg::PipelineExited(self.id, sender)); + self.compositor_proxy.send(CompositorMsg::PipelineExited( + self.top_level_browsing_context_id, + self.id, + sender, + )); if let Err(e) = receiver.recv() { warn!("Sending exit message failed ({:?}).", e); } @@ -455,7 +458,8 @@ impl Pipeline { /// running timers at a heavily limited rate. pub fn set_throttled(&self, throttled: bool) { let script_msg = ScriptThreadMessage::SetThrottled(self.id, throttled); - let compositor_msg = CompositorMsg::SetThrottled(self.id, throttled); + let compositor_msg = + CompositorMsg::SetThrottled(self.top_level_browsing_context_id, self.id, throttled); let err = self.event_loop.send(script_msg); if let Err(e) = err { warn!("Sending SetThrottled to script failed ({}).", e); diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 98637f7e96c..1112d6ec989 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -794,8 +794,11 @@ impl LayoutThread { self.paint_time_metrics .maybe_observe_paint_time(self, epoch, is_contentful.0); - self.compositor_api - .send_display_list(compositor_info, builder.end().1); + self.compositor_api.send_display_list( + self.webview_id, + compositor_info, + builder.end().1, + ); let (keys, instance_keys) = self .font_context @@ -1040,6 +1043,7 @@ impl LayoutThread { let point = Point2D::new(-state.scroll_offset.x, -state.scroll_offset.y); self.compositor_api.send_scroll_node( + self.webview_id, self.id.into(), units::LayoutPoint::from_untyped(point), state.scroll_id, diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index ab709bce7c8..6a3ae728038 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -820,6 +820,7 @@ impl LayoutThread { .insert(state.scroll_id, state.scroll_offset); let point = Point2D::new(-state.scroll_offset.x, -state.scroll_offset.y); self.compositor_api.send_scroll_node( + self.webview_id, self.id.into(), units::LayoutPoint::from_untyped(point), state.scroll_id, @@ -890,8 +891,11 @@ impl LayoutThread { .maybe_observe_paint_time(self, epoch, is_contentful); if reflow_goal.needs_display() { - self.compositor_api - .send_display_list(display_list.compositor_info, display_list.wr.end().1); + self.compositor_api.send_display_list( + self.webview_id, + display_list.compositor_info, + display_list.wr.end().1, + ); let (keys, instance_keys) = self .font_context diff --git a/components/metrics/lib.rs b/components/metrics/lib.rs index 8b09e06bd16..d8237fe2deb 100644 --- a/components/metrics/lib.rs +++ b/components/metrics/lib.rs @@ -9,7 +9,7 @@ use std::time::Duration; use base::Epoch; use base::cross_process_instant::CrossProcessInstant; -use base::id::PipelineId; +use base::id::{PipelineId, WebViewId}; use ipc_channel::ipc::IpcSender; use log::warn; use malloc_size_of_derive::MallocSizeOf; @@ -255,6 +255,7 @@ pub struct PaintTimeMetrics { navigation_start: CrossProcessInstant, first_paint: Cell>, first_contentful_paint: Cell>, + webview_id: WebViewId, pipeline_id: PipelineId, time_profiler_chan: ProfilerChan, constellation_chan: IpcSender, @@ -264,6 +265,7 @@ pub struct PaintTimeMetrics { impl PaintTimeMetrics { pub fn new( + webview_id: WebViewId, pipeline_id: PipelineId, time_profiler_chan: ProfilerChan, constellation_chan: IpcSender, @@ -276,6 +278,7 @@ impl PaintTimeMetrics { navigation_start, first_paint: Cell::new(None), first_contentful_paint: Cell::new(None), + webview_id, pipeline_id, time_profiler_chan, constellation_chan, @@ -308,7 +311,7 @@ impl PaintTimeMetrics { // Send the pending metric information to the compositor thread. // The compositor will record the current time after painting the // frame with the given ID and will send the metric back to us. - let msg = LayoutMsg::PendingPaintMetric(self.pipeline_id, epoch); + let msg = LayoutMsg::PendingPaintMetric(self.webview_id, self.pipeline_id, epoch); if let Err(e) = self.constellation_chan.send(msg) { warn!("Failed to send PendingPaintMetric {:?}", e); } diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 56a588fb157..bdeec8c7160 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -3051,6 +3051,7 @@ impl ScriptThread { }; let paint_time_metrics = PaintTimeMetrics::new( + incomplete.top_level_browsing_context_id, incomplete.pipeline_id, self.senders.time_profiler_sender.clone(), self.senders.layout_to_constellation_ipc_sender.clone(), diff --git a/components/shared/compositing/lib.rs b/components/shared/compositing/lib.rs index 29700c0f1dc..737b105b1ba 100644 --- a/components/shared/compositing/lib.rs +++ b/components/shared/compositing/lib.rs @@ -9,7 +9,7 @@ mod constellation_msg; use std::fmt::{Debug, Error, Formatter}; use base::Epoch; -use base::id::{PipelineId, TopLevelBrowsingContextId}; +use base::id::{PipelineId, TopLevelBrowsingContextId, WebViewId}; pub use constellation_msg::ConstellationMsg; use crossbeam_channel::{Receiver, Sender}; use embedder_traits::{EventLoopWaker, MouseButton, MouseButtonAction}; @@ -59,7 +59,7 @@ impl CompositorReceiver { /// Messages from (or via) the constellation thread to the compositor. pub enum CompositorMsg { /// Alerts the compositor that the given pipeline has changed whether it is running animations. - ChangeRunningAnimationsState(PipelineId, AnimationState), + ChangeRunningAnimationsState(WebViewId, PipelineId, AnimationState), /// Create or update a webview, given its frame tree. CreateOrUpdateWebView(SendableFrameTree), /// Remove a webview. @@ -71,7 +71,7 @@ pub enum CompositorMsg { /// A reply to the compositor asking if the output image is stable. IsReadyToSaveImageReply(bool), /// Set whether to use less resources by stopping animations. - SetThrottled(PipelineId, bool), + SetThrottled(WebViewId, PipelineId, bool), /// WebRender has produced a new frame. This message informs the compositor that /// the frame is ready. It contains a bool to indicate if it needs to composite and the /// `DocumentId` of the new frame. @@ -81,11 +81,11 @@ pub enum CompositorMsg { // when it shuts down a pipeline, to the compositor; when the compositor // sends a reply on the IpcSender, the constellation knows it's safe to // tear down the other threads associated with this pipeline. - PipelineExited(PipelineId, IpcSender<()>), + PipelineExited(WebViewId, PipelineId, IpcSender<()>), /// Indicates to the compositor that it needs to record the time when the frame with /// the given ID (epoch) is painted and report it to the layout of the given - /// pipeline ID. - PendingPaintMetric(PipelineId, Epoch), + /// WebViewId and PipelienId. + PendingPaintMetric(WebViewId, PipelineId, Epoch), /// The load of a page has completed LoadComplete(TopLevelBrowsingContextId), /// WebDriver mouse button event @@ -114,7 +114,7 @@ pub struct CompositionPipeline { impl Debug for CompositorMsg { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { match *self { - CompositorMsg::ChangeRunningAnimationsState(_, state) => { + CompositorMsg::ChangeRunningAnimationsState(_, _, state) => { write!(f, "ChangeRunningAnimationsState({:?})", state) }, CompositorMsg::CreateOrUpdateWebView(..) => write!(f, "CreateOrUpdateWebView"), diff --git a/components/shared/script/script_msg.rs b/components/shared/script/script_msg.rs index f6acdd921f2..5519fd915df 100644 --- a/components/shared/script/script_msg.rs +++ b/components/shared/script/script_msg.rs @@ -9,7 +9,7 @@ use base::Epoch; use base::id::{ BroadcastChannelRouterId, BrowsingContextId, HistoryStateId, MessagePortId, MessagePortRouterId, PipelineId, ServiceWorkerId, ServiceWorkerRegistrationId, - TopLevelBrowsingContextId, + TopLevelBrowsingContextId, WebViewId, }; use canvas_traits::canvas::{CanvasId, CanvasMsg}; use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId}; @@ -49,7 +49,7 @@ pub struct IFrameSizeMsg { pub enum LayoutMsg { /// Requests that the constellation inform the compositor that it needs to record /// the time when the frame with the given ID (epoch) is painted. - PendingPaintMetric(PipelineId, Epoch), + PendingPaintMetric(WebViewId, PipelineId, Epoch), } impl fmt::Debug for LayoutMsg { diff --git a/components/shared/webrender/lib.rs b/components/shared/webrender/lib.rs index e01e635eeb4..8d3045e8f67 100644 --- a/components/shared/webrender/lib.rs +++ b/components/shared/webrender/lib.rs @@ -11,7 +11,7 @@ use core::fmt; use std::collections::HashMap; use std::sync::{Arc, Mutex}; -use base::id::PipelineId; +use base::id::{PipelineId, WebViewId}; use display_list::{CompositorDisplayListInfo, ScrollTreeNodeId}; use embedder_traits::Cursor; use euclid::default::Size2D as UntypedSize2D; @@ -33,9 +33,16 @@ pub enum CrossProcessCompositorMessage { /// Inform WebRender of the existence of this pipeline. SendInitialTransaction(WebRenderPipelineId), /// Perform a scroll operation. - SendScrollNode(WebRenderPipelineId, LayoutPoint, ExternalScrollId), + SendScrollNode( + WebViewId, + WebRenderPipelineId, + LayoutPoint, + ExternalScrollId, + ), /// Inform WebRender of a new display list for the given pipeline. SendDisplayList { + /// The [`WebViewId`] that this display list belongs to. + webview_id: WebViewId, /// The [CompositorDisplayListInfo] that describes the display list being sent. display_list_info: Box, /// A descriptor of this display list used to construct this display list from raw data. @@ -138,11 +145,13 @@ impl CrossProcessCompositorApi { /// Perform a scroll operation. pub fn send_scroll_node( &self, + webview_id: WebViewId, pipeline_id: WebRenderPipelineId, point: LayoutPoint, scroll_id: ExternalScrollId, ) { if let Err(e) = self.0.send(CrossProcessCompositorMessage::SendScrollNode( + webview_id, pipeline_id, point, scroll_id, @@ -154,12 +163,14 @@ impl CrossProcessCompositorApi { /// Inform WebRender of a new display list for the given pipeline. pub fn send_display_list( &self, + webview_id: WebViewId, display_list_info: CompositorDisplayListInfo, list: BuiltDisplayList, ) { let (display_list_data, display_list_descriptor) = list.into_data(); let (display_list_sender, display_list_receiver) = ipc::bytes_channel().unwrap(); if let Err(e) = self.0.send(CrossProcessCompositorMessage::SendDisplayList { + webview_id, display_list_info: Box::new(display_list_info), display_list_descriptor, display_list_receiver, diff --git a/components/webdriver_server/actions.rs b/components/webdriver_server/actions.rs index 26399ff5db9..8e3f6751101 100644 --- a/components/webdriver_server/actions.rs +++ b/components/webdriver_server/actions.rs @@ -431,14 +431,8 @@ impl Handler { target_y: i64, tick_start: Instant, ) { - let pointer_input_state = match self - .session - .as_mut() - .unwrap() - .input_state_table - .get_mut(source_id) - .unwrap() - { + let session = self.session.as_mut().unwrap(); + let pointer_input_state = match session.input_state_table.get_mut(source_id).unwrap() { InputSourceState::Null => unreachable!(), InputSourceState::Key(_) => unreachable!(), InputSourceState::Pointer(pointer_input_state) => pointer_input_state, diff --git a/tests/unit/metrics/paint_time.rs b/tests/unit/metrics/paint_time.rs index e39319da397..ed5e151ae16 100644 --- a/tests/unit/metrics/paint_time.rs +++ b/tests/unit/metrics/paint_time.rs @@ -4,7 +4,7 @@ use base::Epoch; use base::cross_process_instant::CrossProcessInstant; -use base::id::TEST_PIPELINE_ID; +use base::id::{TEST_PIPELINE_ID, TEST_WEBVIEW_ID}; use ipc_channel::ipc; use metrics::{PaintTimeMetrics, ProfilerMetadataFactory, ProgressiveWebMetric}; use profile_traits::time::{ProfilerChan, TimerMetadata}; @@ -25,6 +25,7 @@ fn test_paint_metrics_construction() { let (script_sender, _) = ipc::channel().unwrap(); let start_time = CrossProcessInstant::now(); let paint_time_metrics = PaintTimeMetrics::new( + TEST_WEBVIEW_ID, TEST_PIPELINE_ID, profiler_chan, layout_sender, @@ -56,6 +57,7 @@ fn test_common(display_list_is_contentful: bool, epoch: Epoch) -> PaintTimeMetri let (script_sender, _) = ipc::channel().unwrap(); let start_time = CrossProcessInstant::now(); let mut paint_time_metrics = PaintTimeMetrics::new( + TEST_WEBVIEW_ID, TEST_PIPELINE_ID, profiler_chan, layout_sender,