mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
compositor: Make PipelineDetails
and pending paint metrics per-WebView (#35701)
This is one of the first big steps toward making the compositor work per-WebView. It moves the collection of pipelines into the per-WebView data structure in the compositor as well as the pending paint metrics. This means that more messages need to carry information about the WebView they apply to. Note that there are still a few places that we need to map from `PipelineId` to `WebViewId`, so this also includes a shared mapping which tracks this. The mapping can be removed once event handling is fully per-WebView. Signed-off-by: Martin Robinson <mrobinson@igalia.com> Co-authored-by: Delan Azabani <dazabani@igalia.com>
This commit is contained in:
parent
0d0bcdeb4d
commit
f3e6e4f04e
13 changed files with 537 additions and 373 deletions
|
@ -2,7 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* 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::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::{File, create_dir_all};
|
use std::fs::{File, create_dir_all};
|
||||||
|
@ -26,7 +26,7 @@ use embedder_traits::{
|
||||||
ShutdownState, TouchEvent, TouchEventType, TouchId,
|
ShutdownState, TouchEvent, TouchEventType, TouchId,
|
||||||
};
|
};
|
||||||
use euclid::{Box2D, Point2D, Rect, Scale, Size2D, Transform3D, Vector2D};
|
use euclid::{Box2D, Point2D, Rect, Scale, Size2D, Transform3D, Vector2D};
|
||||||
use fnv::{FnvHashMap, FnvHashSet};
|
use fnv::FnvHashMap;
|
||||||
use ipc_channel::ipc::{self, IpcSharedMemory};
|
use ipc_channel::ipc::{self, IpcSharedMemory};
|
||||||
use libc::c_void;
|
use libc::c_void;
|
||||||
use log::{debug, info, trace, warn};
|
use log::{debug, info, trace, warn};
|
||||||
|
@ -92,6 +92,11 @@ pub struct ServoRenderer {
|
||||||
/// Our top-level browsing contexts.
|
/// Our top-level browsing contexts.
|
||||||
webviews: WebViewManager<WebView>,
|
webviews: WebViewManager<WebView>,
|
||||||
|
|
||||||
|
/// 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<RefCell<HashMap<PipelineId, WebViewId>>>,
|
||||||
|
|
||||||
/// Tracks whether we are in the process of shutting down, or have shut down and should close
|
/// 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.
|
/// the compositor. This is shared with the `Servo` instance.
|
||||||
shutdown_state: Rc<Cell<ShutdownState>>,
|
shutdown_state: Rc<Cell<ShutdownState>>,
|
||||||
|
@ -128,9 +133,6 @@ pub struct IOCompositor {
|
||||||
/// The application window.
|
/// The application window.
|
||||||
pub window: Rc<dyn WindowMethods>,
|
pub window: Rc<dyn WindowMethods>,
|
||||||
|
|
||||||
/// Tracks details about each active pipeline that the compositor knows about.
|
|
||||||
pipeline_details: HashMap<PipelineId, PipelineDetails>,
|
|
||||||
|
|
||||||
/// "Mobile-style" zoom that does not reflow the page.
|
/// "Mobile-style" zoom that does not reflow the page.
|
||||||
viewport_zoom: PinchZoomFactor,
|
viewport_zoom: PinchZoomFactor,
|
||||||
|
|
||||||
|
@ -169,13 +171,6 @@ pub struct IOCompositor {
|
||||||
/// The surfman instance that webrender targets
|
/// The surfman instance that webrender targets
|
||||||
rendering_context: Rc<dyn RenderingContext>,
|
rendering_context: Rc<dyn RenderingContext>,
|
||||||
|
|
||||||
/// 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<PipelineId, Vec<Epoch>>,
|
|
||||||
|
|
||||||
/// The coordinates of the native window, its view and the screen.
|
/// The coordinates of the native window, its view and the screen.
|
||||||
embedder_coordinates: EmbedderCoordinates,
|
embedder_coordinates: EmbedderCoordinates,
|
||||||
|
|
||||||
|
@ -232,39 +227,86 @@ bitflags! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PipelineDetails {
|
pub(crate) struct PipelineDetails {
|
||||||
/// The pipeline associated with this PipelineDetails object.
|
/// The pipeline associated with this PipelineDetails object.
|
||||||
pipeline: Option<CompositionPipeline>,
|
pub pipeline: Option<CompositionPipeline>,
|
||||||
|
|
||||||
|
/// The [`PipelineId`] of this pipeline.
|
||||||
|
pub id: PipelineId,
|
||||||
|
|
||||||
/// The id of the parent pipeline, if any.
|
/// The id of the parent pipeline, if any.
|
||||||
parent_pipeline_id: Option<PipelineId>,
|
pub parent_pipeline_id: Option<PipelineId>,
|
||||||
|
|
||||||
/// The epoch of the most recent display list for this pipeline. Note that this display
|
/// 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.
|
/// list might not be displayed, as WebRender processes display lists asynchronously.
|
||||||
most_recent_display_list_epoch: Option<WebRenderEpoch>,
|
pub most_recent_display_list_epoch: Option<WebRenderEpoch>,
|
||||||
|
|
||||||
/// Whether animations are running
|
/// Whether animations are running
|
||||||
animations_running: bool,
|
pub animations_running: bool,
|
||||||
|
|
||||||
/// Whether there are animation callbacks
|
/// Whether there are animation callbacks
|
||||||
animation_callbacks_running: bool,
|
pub animation_callbacks_running: bool,
|
||||||
|
|
||||||
/// Whether to use less resources by stopping animations.
|
/// 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
|
/// Hit test items for this pipeline. This is used to map WebRender hit test
|
||||||
/// information to the full information necessary for Servo.
|
/// information to the full information necessary for Servo.
|
||||||
hit_test_items: Vec<HitTestInfo>,
|
pub hit_test_items: Vec<HitTestInfo>,
|
||||||
|
|
||||||
/// The compositor-side [ScrollTree]. This is used to allow finding and scrolling
|
/// The compositor-side [ScrollTree]. This is used to allow finding and scrolling
|
||||||
/// nodes in the compositor before forwarding new offsets to WebRender.
|
/// 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<Epoch>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PipelineDetails {
|
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 {
|
PipelineDetails {
|
||||||
pipeline: None,
|
pipeline: None,
|
||||||
|
id,
|
||||||
parent_pipeline_id: None,
|
parent_pipeline_id: None,
|
||||||
most_recent_display_list_epoch: None,
|
most_recent_display_list_epoch: None,
|
||||||
animations_running: false,
|
animations_running: false,
|
||||||
|
@ -272,6 +314,7 @@ impl PipelineDetails {
|
||||||
throttled: false,
|
throttled: false,
|
||||||
hit_test_items: Vec::new(),
|
hit_test_items: Vec::new(),
|
||||||
scroll_tree: ScrollTree::default(),
|
scroll_tree: ScrollTree::default(),
|
||||||
|
pending_paint_metrics: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,6 +353,7 @@ impl IOCompositor {
|
||||||
global: ServoRenderer {
|
global: ServoRenderer {
|
||||||
shutdown_state: state.shutdown_state,
|
shutdown_state: state.shutdown_state,
|
||||||
webviews: WebViewManager::default(),
|
webviews: WebViewManager::default(),
|
||||||
|
pipeline_to_webview_map: Default::default(),
|
||||||
compositor_receiver: state.receiver,
|
compositor_receiver: state.receiver,
|
||||||
constellation_sender: state.constellation_chan,
|
constellation_sender: state.constellation_chan,
|
||||||
time_profiler_chan: state.time_profiler_chan,
|
time_profiler_chan: state.time_profiler_chan,
|
||||||
|
@ -321,7 +365,6 @@ impl IOCompositor {
|
||||||
},
|
},
|
||||||
embedder_coordinates: window.get_coordinates(),
|
embedder_coordinates: window.get_coordinates(),
|
||||||
window,
|
window,
|
||||||
pipeline_details: HashMap::new(),
|
|
||||||
needs_repaint: Cell::default(),
|
needs_repaint: Cell::default(),
|
||||||
touch_handler: TouchHandler::new(),
|
touch_handler: TouchHandler::new(),
|
||||||
pending_scroll_zoom_events: Vec::new(),
|
pending_scroll_zoom_events: Vec::new(),
|
||||||
|
@ -335,7 +378,6 @@ impl IOCompositor {
|
||||||
webrender: Some(state.webrender),
|
webrender: Some(state.webrender),
|
||||||
webrender_document: state.webrender_document,
|
webrender_document: state.webrender_document,
|
||||||
rendering_context: state.rendering_context,
|
rendering_context: state.rendering_context,
|
||||||
pending_paint_metrics: HashMap::new(),
|
|
||||||
cursor: Cursor::None,
|
cursor: Cursor::None,
|
||||||
cursor_pos: DevicePoint::new(0.0, 0.0),
|
cursor_pos: DevicePoint::new(0.0, 0.0),
|
||||||
convert_mouse_to_touch,
|
convert_mouse_to_touch,
|
||||||
|
@ -378,22 +420,24 @@ impl IOCompositor {
|
||||||
Some(cursor) if cursor != self.cursor => cursor,
|
Some(cursor) if cursor != self.cursor => cursor,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(webview_id) = self
|
let Some(webview_id) = self
|
||||||
.pipeline_details(result.pipeline_id)
|
.global
|
||||||
.pipeline
|
.pipeline_to_webview_map
|
||||||
.as_ref()
|
.borrow()
|
||||||
.map(|composition_pipeline| composition_pipeline.top_level_browsing_context_id)
|
.get(&result.pipeline_id)
|
||||||
|
.cloned()
|
||||||
else {
|
else {
|
||||||
warn!(
|
warn!("Couldn't update cursor for non-WebView-associated pipeline");
|
||||||
"Updating cursor for not-yet-rendered pipeline: {}",
|
|
||||||
result.pipeline_id
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.cursor = cursor;
|
self.cursor = cursor;
|
||||||
let msg = ConstellationMsg::SetCursor(webview_id, cursor);
|
if let Err(e) = self
|
||||||
if let Err(e) = self.global.constellation_sender.send(msg) {
|
.global
|
||||||
|
.constellation_sender
|
||||||
|
.send(ConstellationMsg::SetCursor(webview_id, cursor))
|
||||||
|
{
|
||||||
warn!("Sending event to constellation failed ({:?}).", e);
|
warn!("Sending event to constellation failed ({:?}).", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -433,13 +477,34 @@ impl IOCompositor {
|
||||||
}
|
}
|
||||||
|
|
||||||
match msg {
|
match msg {
|
||||||
CompositorMsg::ChangeRunningAnimationsState(pipeline_id, animation_state) => {
|
CompositorMsg::ChangeRunningAnimationsState(
|
||||||
self.change_running_animations_state(pipeline_id, animation_state);
|
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) => {
|
CompositorMsg::CreateOrUpdateWebView(frame_tree) => {
|
||||||
self.set_frame_tree_for_webview(&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) => {
|
CompositorMsg::RemoveWebView(top_level_browsing_context_id) => {
|
||||||
|
@ -474,14 +539,21 @@ impl IOCompositor {
|
||||||
self.set_needs_repaint(RepaintReason::ReadyForScreenshot);
|
self.set_needs_repaint(RepaintReason::ReadyForScreenshot);
|
||||||
},
|
},
|
||||||
|
|
||||||
CompositorMsg::SetThrottled(pipeline_id, throttled) => {
|
CompositorMsg::SetThrottled(webview_id, pipeline_id, throttled) => {
|
||||||
self.pipeline_details(pipeline_id).throttled = throttled;
|
if let Some(webview) = self.global.webviews.get_mut(webview_id) {
|
||||||
self.process_animations(true);
|
webview.set_throttled(pipeline_id, throttled);
|
||||||
|
self.process_animations(true);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
CompositorMsg::PipelineExited(pipeline_id, sender) => {
|
CompositorMsg::PipelineExited(webview_id, pipeline_id, sender) => {
|
||||||
debug!("Compositor got pipeline exited: {:?}", pipeline_id);
|
debug!(
|
||||||
self.remove_pipeline_root_layer(pipeline_id);
|
"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(());
|
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);
|
self.set_needs_repaint(RepaintReason::NewWebRenderFrame);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -521,11 +593,10 @@ impl IOCompositor {
|
||||||
self.dispatch_input_event(InputEvent::MouseMove(MouseMoveEvent { point }));
|
self.dispatch_input_event(InputEvent::MouseMove(MouseMoveEvent { point }));
|
||||||
},
|
},
|
||||||
|
|
||||||
CompositorMsg::PendingPaintMetric(pipeline_id, epoch) => {
|
CompositorMsg::PendingPaintMetric(webview_id, pipeline_id, epoch) => {
|
||||||
self.pending_paint_metrics
|
if let Some(webview) = self.global.webviews.get_mut(webview_id) {
|
||||||
.entry(pipeline_id)
|
webview.add_pending_paint_metric(pipeline_id, epoch);
|
||||||
.or_default()
|
}
|
||||||
.push(epoch);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
CompositorMsg::CrossProcess(cross_proces_message) => {
|
CompositorMsg::CrossProcess(cross_proces_message) => {
|
||||||
|
@ -552,14 +623,18 @@ impl IOCompositor {
|
||||||
},
|
},
|
||||||
|
|
||||||
CrossProcessCompositorMessage::SendScrollNode(
|
CrossProcessCompositorMessage::SendScrollNode(
|
||||||
|
webview_id,
|
||||||
pipeline_id,
|
pipeline_id,
|
||||||
point,
|
point,
|
||||||
external_scroll_id,
|
external_scroll_id,
|
||||||
) => {
|
) => {
|
||||||
|
let Some(webview) = self.global.webviews.get_mut(webview_id) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let pipeline_id = pipeline_id.into();
|
let pipeline_id = pipeline_id.into();
|
||||||
let pipeline_details = match self.pipeline_details.get_mut(&pipeline_id) {
|
let Some(pipeline_details) = webview.pipelines.get_mut(&pipeline_id) else {
|
||||||
Some(details) => details,
|
return;
|
||||||
None => return,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let offset = LayoutVector2D::new(point.x, point.y);
|
let offset = LayoutVector2D::new(point.x, point.y);
|
||||||
|
@ -589,6 +664,7 @@ impl IOCompositor {
|
||||||
},
|
},
|
||||||
|
|
||||||
CrossProcessCompositorMessage::SendDisplayList {
|
CrossProcessCompositorMessage::SendDisplayList {
|
||||||
|
webview_id,
|
||||||
display_list_info,
|
display_list_info,
|
||||||
display_list_descriptor,
|
display_list_descriptor,
|
||||||
display_list_receiver,
|
display_list_receiver,
|
||||||
|
@ -633,8 +709,13 @@ impl IOCompositor {
|
||||||
servo_profiling = true,
|
servo_profiling = true,
|
||||||
)
|
)
|
||||||
.entered();
|
.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 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.most_recent_display_list_epoch = Some(display_list_info.epoch);
|
||||||
details.hit_test_items = display_list_info.hit_test_info;
|
details.hit_test_items = display_list_info.hit_test_info;
|
||||||
details.install_new_scroll_tree(display_list_info.scroll_tree);
|
details.install_new_scroll_tree(display_list_info.scroll_tree);
|
||||||
|
@ -777,9 +858,14 @@ impl IOCompositor {
|
||||||
/// compositor no longer does any WebRender frame generation.
|
/// compositor no longer does any WebRender frame generation.
|
||||||
fn handle_browser_message_while_shutting_down(&mut self, msg: CompositorMsg) {
|
fn handle_browser_message_while_shutting_down(&mut self, msg: CompositorMsg) {
|
||||||
match msg {
|
match msg {
|
||||||
CompositorMsg::PipelineExited(pipeline_id, sender) => {
|
CompositorMsg::PipelineExited(webview_id, pipeline_id, sender) => {
|
||||||
debug!("Compositor got pipeline exited: {:?}", pipeline_id);
|
debug!(
|
||||||
self.remove_pipeline_root_layer(pipeline_id);
|
"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(());
|
let _ = sender.send(());
|
||||||
},
|
},
|
||||||
CompositorMsg::CrossProcess(CrossProcessCompositorMessage::GenerateImageKey(
|
CompositorMsg::CrossProcess(CrossProcessCompositorMessage::GenerateImageKey(
|
||||||
|
@ -838,61 +924,6 @@ impl IOCompositor {
|
||||||
transaction.generate_frame(0, true /* present */, reason);
|
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
|
/// 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
|
/// for each visible top-level browsing context, applying a transformation on the root for
|
||||||
/// pinch zoom, page zoom, and HiDPI scaling.
|
/// 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 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]);
|
let clip_chain_id = builder.define_clip_chain(None, [root_clip_id]);
|
||||||
for (_, webview) in self.global.webviews.painting_order() {
|
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;
|
let scaled_webview_rect = webview.rect / zoom_factor;
|
||||||
builder.push_iframe(
|
builder.push_iframe(
|
||||||
LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()),
|
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
|
/// TODO(mrobinson): Could we only send offsets for the branch being modified
|
||||||
/// and not the entire scene?
|
/// and not the entire scene?
|
||||||
fn update_transaction_with_all_scroll_offsets(&self, transaction: &mut Transaction) {
|
fn update_transaction_with_all_scroll_offsets(&self, transaction: &mut Transaction) {
|
||||||
for details in self.pipeline_details.values() {
|
for webview in self.global.webviews.iter() {
|
||||||
for node in details.scroll_tree.nodes.iter() {
|
for details in webview.pipelines.values() {
|
||||||
let (Some(offset), Some(external_id)) = (node.offset(), node.external_id()) else {
|
for node in details.scroll_tree.nodes.iter() {
|
||||||
continue;
|
let (Some(offset), Some(external_id)) = (node.offset(), node.external_id())
|
||||||
};
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
let offset = LayoutVector2D::new(-offset.x, -offset.y);
|
let offset = LayoutVector2D::new(-offset.x, -offset.y);
|
||||||
transaction.set_scroll_offsets(
|
transaction.set_scroll_offsets(
|
||||||
external_id,
|
external_id,
|
||||||
vec![SampledScrollOffset {
|
vec![SampledScrollOffset {
|
||||||
offset,
|
offset,
|
||||||
generation: 0,
|
generation: 0,
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -996,8 +1030,11 @@ impl IOCompositor {
|
||||||
pub fn add_webview(&mut self, webview_id: WebViewId) {
|
pub fn add_webview(&mut self, webview_id: WebViewId) {
|
||||||
let size = self.rendering_context.size2d().to_f32();
|
let size = self.rendering_context.size2d().to_f32();
|
||||||
self.global.webviews.entry(webview_id).or_insert(WebView {
|
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),
|
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;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_pipeline_id = Some(frame_tree.pipeline.id);
|
webview.set_frame_tree(frame_tree);
|
||||||
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;
|
|
||||||
|
|
||||||
self.send_root_pipeline_display_list();
|
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) {
|
fn remove_webview(&mut self, webview_id: WebViewId) {
|
||||||
debug!("{}: Removing", top_level_browsing_context_id);
|
debug!("{}: Removing", webview_id);
|
||||||
let Ok(webview) = self.global.webviews.remove(top_level_browsing_context_id) else {
|
if self.global.webviews.remove(webview_id).is_err() {
|
||||||
warn!("{top_level_browsing_context_id}: Removing unknown webview");
|
warn!("{webview_id}: Removing unknown webview");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.send_root_pipeline_display_list();
|
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) {
|
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<PipelineId>,
|
|
||||||
frame_tree: &SendableFrameTree,
|
|
||||||
) {
|
|
||||||
pipelines.insert(frame_tree.pipeline.id);
|
|
||||||
for kid in &frame_tree.children {
|
|
||||||
collect_pipelines(pipelines, kid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut attached_pipelines: FnvHashSet<PipelineId> = 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<PipelineId>,
|
|
||||||
) {
|
|
||||||
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::<Vec<_>>();
|
|
||||||
|
|
||||||
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) {
|
pub fn on_embedder_window_moved(&mut self) {
|
||||||
self.embedder_coordinates = self.window.get_coordinates();
|
self.embedder_coordinates = self.window.get_coordinates();
|
||||||
}
|
}
|
||||||
|
@ -1333,10 +1294,7 @@ impl IOCompositor {
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|item| {
|
.filter_map(|item| {
|
||||||
let pipeline_id = item.pipeline.into();
|
let pipeline_id = item.pipeline.into();
|
||||||
let details = match self.pipeline_details.get(&pipeline_id) {
|
let details = self.details_for_pipeline(pipeline_id)?;
|
||||||
Some(details) => details,
|
|
||||||
None => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// If the epoch in the tag does not match the current epoch of the pipeline,
|
// 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
|
// then the hit test is against an old version of the display list and we
|
||||||
|
@ -1760,7 +1718,7 @@ impl IOCompositor {
|
||||||
generation: 0,
|
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);
|
self.generate_frame(&mut transaction, RenderReasons::APZ);
|
||||||
|
@ -1804,10 +1762,9 @@ impl IOCompositor {
|
||||||
..
|
..
|
||||||
} in hit_test_results.iter()
|
} 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) {
|
if previous_pipeline_id.replace(pipeline_id) != Some(pipeline_id) {
|
||||||
let scroll_result = self
|
let scroll_result = pipeline_details
|
||||||
.pipeline_details
|
|
||||||
.get_mut(pipeline_id)?
|
|
||||||
.scroll_tree
|
.scroll_tree
|
||||||
.scroll_node_or_ancestor(scroll_tree_node, scroll_location);
|
.scroll_node_or_ancestor(scroll_tree_node, scroll_location);
|
||||||
if let Some((external_id, offset)) = scroll_result {
|
if let Some((external_id, offset)) = scroll_result {
|
||||||
|
@ -1831,50 +1788,24 @@ impl IOCompositor {
|
||||||
}
|
}
|
||||||
self.last_animation_tick = Instant::now();
|
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")]
|
#[cfg(feature = "webxr")]
|
||||||
let webxr_running = self.global.webxr_main_thread.running();
|
let webxr_running = self.global.webxr_main_thread.running();
|
||||||
#[cfg(not(feature = "webxr"))]
|
#[cfg(not(feature = "webxr"))]
|
||||||
let webxr_running = false;
|
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
|
windowing::AnimationState::Idle
|
||||||
} else {
|
} else {
|
||||||
windowing::AnimationState::Animating
|
windowing::AnimationState::Animating
|
||||||
};
|
};
|
||||||
|
|
||||||
self.window.set_animation_state(animation_state);
|
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<f32, DeviceIndependentPixel, DevicePixel> {
|
fn hidpi_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> {
|
||||||
|
@ -1943,10 +1874,40 @@ impl IOCompositor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_scroll_positions_to_layout_for_pipeline(&self, pipeline_id: &PipelineId) {
|
fn details_for_pipeline(&self, pipeline_id: PipelineId) -> Option<&PipelineDetails> {
|
||||||
let details = match self.pipeline_details.get(pipeline_id) {
|
let webview_id = self
|
||||||
Some(details) => details,
|
.global
|
||||||
None => return,
|
.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();
|
let mut scroll_states = Vec::new();
|
||||||
|
@ -1960,32 +1921,25 @@ impl IOCompositor {
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(pipeline) = details.pipeline.as_ref() {
|
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);
|
let _ = pipeline.script_chan.send(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if any pipelines currently have active animations or animation callbacks.
|
// Check if any pipelines currently have active animations or animation callbacks.
|
||||||
fn animations_active(&self) -> bool {
|
fn animations_or_animation_callbacks_running(&self) -> bool {
|
||||||
for details in self.pipeline_details.values() {
|
self.global
|
||||||
// If animations are currently running, then don't bother checking
|
.webviews
|
||||||
// with the constellation if the output image is stable.
|
.iter()
|
||||||
if details.animations_running {
|
.any(WebView::animations_or_animation_callbacks_running)
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if details.animation_callbacks_running {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if any animation callbacks (ie `requestAnimationFrame`) are waiting for a response.
|
/// Returns true if any animation callbacks (ie `requestAnimationFrame`) are waiting for a response.
|
||||||
fn animation_callbacks_active(&self) -> bool {
|
fn animation_callbacks_running(&self) -> bool {
|
||||||
self.pipeline_details
|
self.global
|
||||||
.values()
|
.webviews
|
||||||
.any(|details| details.animation_callbacks_running)
|
.iter()
|
||||||
|
.any(WebView::animation_callbacks_running)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Query the constellation to see if the current compositor
|
/// 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
|
// This gets sent to the constellation for comparison with the current
|
||||||
// frame tree.
|
// frame tree.
|
||||||
let mut pipeline_epochs = HashMap::new();
|
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
|
if let Some(WebRenderEpoch(epoch)) = self
|
||||||
.webrender
|
.webrender
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -2111,7 +2065,7 @@ impl IOCompositor {
|
||||||
// The current image may be ready to output. However, if there are animations active,
|
// 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
|
// tick those instead and continue waiting for the image output to be stable AND
|
||||||
// all active animations to complete.
|
// all active animations to complete.
|
||||||
if self.animations_active() {
|
if self.animations_or_animation_callbacks_running() {
|
||||||
self.process_animations(false);
|
self.process_animations(false);
|
||||||
return Err(UnableToComposite::NotReadyToPaintImage(
|
return Err(UnableToComposite::NotReadyToPaintImage(
|
||||||
NotReadyToPaint::AnimationsActive,
|
NotReadyToPaint::AnimationsActive,
|
||||||
|
@ -2153,62 +2107,51 @@ impl IOCompositor {
|
||||||
/// current time, inform the constellation about it and remove the pending metric from
|
/// current time, inform the constellation about it and remove the pending metric from
|
||||||
/// the list.
|
/// the list.
|
||||||
fn send_pending_paint_metrics_messages_after_composite(&mut self) {
|
fn send_pending_paint_metrics_messages_after_composite(&mut self) {
|
||||||
if self.pending_paint_metrics.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let paint_time = CrossProcessInstant::now();
|
let paint_time = CrossProcessInstant::now();
|
||||||
let mut pipelines_to_remove = Vec::new();
|
for webview_details in self.global.webviews.iter_mut() {
|
||||||
let pending_paint_metrics = &mut self.pending_paint_metrics;
|
// 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
|
let Some(WebRenderEpoch(current_epoch)) = self
|
||||||
// epoch and update paint timing if necessary.
|
.webrender
|
||||||
for (pipeline_id, pending_epochs) in pending_paint_metrics.iter_mut() {
|
.as_ref()
|
||||||
let Some(WebRenderEpoch(current_epoch)) = self
|
.and_then(|wr| wr.current_epoch(self.webrender_document, pipeline_id.into()))
|
||||||
.webrender
|
else {
|
||||||
.as_ref()
|
continue;
|
||||||
.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 current_epoch = Epoch(current_epoch);
|
||||||
let Some(pipeline) = self
|
let Some(index) = pipeline
|
||||||
.pipeline_details
|
.pending_paint_metrics
|
||||||
.get(pipeline_id)
|
.iter()
|
||||||
.and_then(|pipeline_details| pipeline_details.pipeline.as_ref())
|
.position(|epoch| *epoch == current_epoch)
|
||||||
else {
|
else {
|
||||||
pipelines_to_remove.push(*pipeline_id);
|
continue;
|
||||||
continue;
|
};
|
||||||
};
|
|
||||||
|
|
||||||
let current_epoch = Epoch(current_epoch);
|
// Remove all epochs that were pending before the current epochs. They were not and will not,
|
||||||
let Some(index) = pending_epochs
|
// be painted.
|
||||||
.iter()
|
pipeline.pending_paint_metrics.drain(0..index);
|
||||||
.position(|epoch| *epoch == current_epoch)
|
|
||||||
else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Remove all epochs that were pending before the current epochs. They were not and will not,
|
if let Err(error) =
|
||||||
// be painted.
|
composition_pipeline
|
||||||
pending_epochs.drain(0..index);
|
.script_chan
|
||||||
|
.send(ScriptThreadMessage::SetEpochPaintTime(
|
||||||
if let Err(error) = pipeline
|
*pipeline_id,
|
||||||
.script_chan
|
current_epoch,
|
||||||
.send(ScriptThreadMessage::SetEpochPaintTime(
|
paint_time,
|
||||||
*pipeline_id,
|
))
|
||||||
current_epoch,
|
{
|
||||||
paint_time,
|
warn!("Sending RequestLayoutPaintMetric message to layout failed ({error:?}).");
|
||||||
))
|
}
|
||||||
{
|
|
||||||
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) {
|
fn clear_background(&self) {
|
||||||
|
|
|
@ -2,19 +2,190 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
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 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)]
|
use crate::IOCompositor;
|
||||||
pub struct WebView {
|
use crate::compositor::PipelineDetails;
|
||||||
pub pipeline_id: Option<PipelineId>,
|
|
||||||
|
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<PipelineId>,
|
||||||
pub rect: DeviceRect,
|
pub rect: DeviceRect,
|
||||||
|
/// Tracks details about each active pipeline that the compositor knows about.
|
||||||
|
pub pipelines: HashMap<PipelineId, PipelineDetails>,
|
||||||
|
/// 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<RefCell<HashMap<PipelineId, WebViewId>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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<PipelineId>,
|
||||||
|
) {
|
||||||
|
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<PipelineId>,
|
||||||
|
frame_tree: &SendableFrameTree,
|
||||||
|
) {
|
||||||
|
pipelines.insert(frame_tree.pipeline.id);
|
||||||
|
for kid in &frame_tree.children {
|
||||||
|
collect_pipelines(pipelines, kid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut attached_pipelines: FnvHashSet<PipelineId> = 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<WebView> {
|
pub struct WebViewManager<WebView> {
|
||||||
/// Our top-level browsing contexts. In the WebRender scene, their pipelines are the children of
|
/// 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.
|
/// a single root pipeline that also applies any pinch zoom transformation.
|
||||||
|
@ -27,6 +198,15 @@ pub struct WebViewManager<WebView> {
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct UnknownWebView(pub WebViewId);
|
pub struct UnknownWebView(pub WebViewId);
|
||||||
|
|
||||||
|
impl<WebView> Default for WebViewManager<WebView> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
webviews: Default::default(),
|
||||||
|
painting_order: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<WebView> WebViewManager<WebView> {
|
impl<WebView> WebViewManager<WebView> {
|
||||||
pub fn remove(&mut self, webview_id: WebViewId) -> Result<WebView, UnknownWebView> {
|
pub fn remove(&mut self, webview_id: WebViewId) -> Result<WebView, UnknownWebView> {
|
||||||
self.painting_order.retain(|b| *b != webview_id);
|
self.painting_order.retain(|b| *b != webview_id);
|
||||||
|
@ -98,6 +278,14 @@ impl<WebView> WebViewManager<WebView> {
|
||||||
pub fn entry(&mut self, webview_id: WebViewId) -> Entry<'_, WebViewId, WebView> {
|
pub fn entry(&mut self, webview_id: WebViewId) -> Entry<'_, WebViewId, WebView> {
|
||||||
self.webviews.entry(webview_id)
|
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)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1446,7 +1446,7 @@ where
|
||||||
let (source_pipeline_id, content) = message;
|
let (source_pipeline_id, content) = message;
|
||||||
trace_script_msg!(content, "{source_pipeline_id}: {content:?}");
|
trace_script_msg!(content, "{source_pipeline_id}: {content:?}");
|
||||||
|
|
||||||
let source_top_ctx_id = match self
|
let webview_id = match self
|
||||||
.pipelines
|
.pipelines
|
||||||
.get(&source_pipeline_id)
|
.get(&source_pipeline_id)
|
||||||
.map(|pipeline| pipeline.top_level_browsing_context_id)
|
.map(|pipeline| pipeline.top_level_browsing_context_id)
|
||||||
|
@ -1521,10 +1521,10 @@ where
|
||||||
self.handle_pipeline_exited(source_pipeline_id);
|
self.handle_pipeline_exited(source_pipeline_id);
|
||||||
},
|
},
|
||||||
FromScriptMsg::DiscardDocument => {
|
FromScriptMsg::DiscardDocument => {
|
||||||
self.handle_discard_document(source_top_ctx_id, source_pipeline_id);
|
self.handle_discard_document(webview_id, source_pipeline_id);
|
||||||
},
|
},
|
||||||
FromScriptMsg::DiscardTopLevelBrowsingContext => {
|
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) => {
|
FromScriptMsg::ScriptLoadedURLInIFrame(load_info) => {
|
||||||
self.handle_script_loaded_url_in_iframe_msg(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.
|
// Ask the embedder for permission to load a new page.
|
||||||
FromScriptMsg::LoadUrl(load_data, history_handling) => {
|
FromScriptMsg::LoadUrl(load_data, history_handling) => {
|
||||||
self.schedule_navigation(
|
self.schedule_navigation(
|
||||||
source_top_ctx_id,
|
webview_id,
|
||||||
source_pipeline_id,
|
source_pipeline_id,
|
||||||
load_data,
|
load_data,
|
||||||
history_handling,
|
history_handling,
|
||||||
|
@ -1552,7 +1552,7 @@ where
|
||||||
},
|
},
|
||||||
// A page loaded has completed all parsing, script, and reflow messages have been sent.
|
// A page loaded has completed all parsing, script, and reflow messages have been sent.
|
||||||
FromScriptMsg::LoadComplete => {
|
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
|
// Handle navigating to a fragment
|
||||||
FromScriptMsg::NavigatedToFragment(new_url, replacement_enabled) => {
|
FromScriptMsg::NavigatedToFragment(new_url, replacement_enabled) => {
|
||||||
|
@ -1560,7 +1560,7 @@ where
|
||||||
},
|
},
|
||||||
// Handle a forward or back request
|
// Handle a forward or back request
|
||||||
FromScriptMsg::TraverseHistory(direction) => {
|
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.
|
// Handle a push history state request.
|
||||||
FromScriptMsg::PushHistoryState(history_state_id, url) => {
|
FromScriptMsg::PushHistoryState(history_state_id, url) => {
|
||||||
|
@ -1571,7 +1571,7 @@ where
|
||||||
},
|
},
|
||||||
// Handle a joint session history length request.
|
// Handle a joint session history length request.
|
||||||
FromScriptMsg::JointSessionHistoryLength(response_sender) => {
|
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
|
// Notification that the new document is ready to become active
|
||||||
FromScriptMsg::ActivateDocument => {
|
FromScriptMsg::ActivateDocument => {
|
||||||
|
@ -1627,7 +1627,7 @@ where
|
||||||
response_sender.send(true).unwrap_or_default();
|
response_sender.send(true).unwrap_or_default();
|
||||||
},
|
},
|
||||||
FromScriptMsg::LogEntry(thread_name, entry) => {
|
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
|
FromScriptMsg::TouchEventProcessed(result) => self
|
||||||
.compositor_proxy
|
.compositor_proxy
|
||||||
|
@ -1714,19 +1714,19 @@ where
|
||||||
}
|
}
|
||||||
self.active_media_session = Some(pipeline_id);
|
self.active_media_session = Some(pipeline_id);
|
||||||
self.embedder_proxy
|
self.embedder_proxy
|
||||||
.send(EmbedderMsg::MediaSessionEvent(source_top_ctx_id, event));
|
.send(EmbedderMsg::MediaSessionEvent(webview_id, event));
|
||||||
},
|
},
|
||||||
#[cfg(feature = "webgpu")]
|
#[cfg(feature = "webgpu")]
|
||||||
FromScriptMsg::RequestAdapter(response_sender, options, ids) => self
|
FromScriptMsg::RequestAdapter(response_sender, options, ids) => self
|
||||||
.handle_wgpu_request(
|
.handle_wgpu_request(
|
||||||
source_pipeline_id,
|
source_pipeline_id,
|
||||||
BrowsingContextId::from(source_top_ctx_id),
|
BrowsingContextId::from(webview_id),
|
||||||
FromScriptMsg::RequestAdapter(response_sender, options, ids),
|
FromScriptMsg::RequestAdapter(response_sender, options, ids),
|
||||||
),
|
),
|
||||||
#[cfg(feature = "webgpu")]
|
#[cfg(feature = "webgpu")]
|
||||||
FromScriptMsg::GetWebGPUChan(response_sender) => self.handle_wgpu_request(
|
FromScriptMsg::GetWebGPUChan(response_sender) => self.handle_wgpu_request(
|
||||||
source_pipeline_id,
|
source_pipeline_id,
|
||||||
BrowsingContextId::from(source_top_ctx_id),
|
BrowsingContextId::from(webview_id),
|
||||||
FromScriptMsg::GetWebGPUChan(response_sender),
|
FromScriptMsg::GetWebGPUChan(response_sender),
|
||||||
),
|
),
|
||||||
FromScriptMsg::TitleChanged(pipeline, title) => {
|
FromScriptMsg::TitleChanged(pipeline, title) => {
|
||||||
|
@ -2008,8 +2008,8 @@ where
|
||||||
fn handle_request_from_layout(&mut self, message: FromLayoutMsg) {
|
fn handle_request_from_layout(&mut self, message: FromLayoutMsg) {
|
||||||
trace_layout_msg!(message, "{message:?}");
|
trace_layout_msg!(message, "{message:?}");
|
||||||
match message {
|
match message {
|
||||||
FromLayoutMsg::PendingPaintMetric(pipeline_id, epoch) => {
|
FromLayoutMsg::PendingPaintMetric(webview_id, pipeline_id, epoch) => {
|
||||||
self.handle_pending_paint_metric(pipeline_id, epoch);
|
self.handle_pending_paint_metric(webview_id, pipeline_id, epoch);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3440,9 +3440,18 @@ where
|
||||||
feature = "tracing",
|
feature = "tracing",
|
||||||
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
|
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
|
self.compositor_proxy
|
||||||
.send(CompositorMsg::PendingPaintMetric(pipeline_id, epoch))
|
.send(CompositorMsg::PendingPaintMetric(
|
||||||
|
webview_id,
|
||||||
|
pipeline_id,
|
||||||
|
epoch,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
|
@ -3468,6 +3477,7 @@ where
|
||||||
pipeline.animation_state = animation_state;
|
pipeline.animation_state = animation_state;
|
||||||
self.compositor_proxy
|
self.compositor_proxy
|
||||||
.send(CompositorMsg::ChangeRunningAnimationsState(
|
.send(CompositorMsg::ChangeRunningAnimationsState(
|
||||||
|
pipeline.top_level_browsing_context_id,
|
||||||
pipeline_id,
|
pipeline_id,
|
||||||
animation_state,
|
animation_state,
|
||||||
))
|
))
|
||||||
|
|
|
@ -386,8 +386,11 @@ impl Pipeline {
|
||||||
// It's OK for the constellation to block on the compositor,
|
// It's OK for the constellation to block on the compositor,
|
||||||
// since the compositor never blocks on the constellation.
|
// since the compositor never blocks on the constellation.
|
||||||
if let Ok((sender, receiver)) = ipc::channel() {
|
if let Ok((sender, receiver)) = ipc::channel() {
|
||||||
self.compositor_proxy
|
self.compositor_proxy.send(CompositorMsg::PipelineExited(
|
||||||
.send(CompositorMsg::PipelineExited(self.id, sender));
|
self.top_level_browsing_context_id,
|
||||||
|
self.id,
|
||||||
|
sender,
|
||||||
|
));
|
||||||
if let Err(e) = receiver.recv() {
|
if let Err(e) = receiver.recv() {
|
||||||
warn!("Sending exit message failed ({:?}).", e);
|
warn!("Sending exit message failed ({:?}).", e);
|
||||||
}
|
}
|
||||||
|
@ -455,7 +458,8 @@ impl Pipeline {
|
||||||
/// running timers at a heavily limited rate.
|
/// running timers at a heavily limited rate.
|
||||||
pub fn set_throttled(&self, throttled: bool) {
|
pub fn set_throttled(&self, throttled: bool) {
|
||||||
let script_msg = ScriptThreadMessage::SetThrottled(self.id, throttled);
|
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);
|
let err = self.event_loop.send(script_msg);
|
||||||
if let Err(e) = err {
|
if let Err(e) = err {
|
||||||
warn!("Sending SetThrottled to script failed ({}).", e);
|
warn!("Sending SetThrottled to script failed ({}).", e);
|
||||||
|
|
|
@ -794,8 +794,11 @@ impl LayoutThread {
|
||||||
self.paint_time_metrics
|
self.paint_time_metrics
|
||||||
.maybe_observe_paint_time(self, epoch, is_contentful.0);
|
.maybe_observe_paint_time(self, epoch, is_contentful.0);
|
||||||
|
|
||||||
self.compositor_api
|
self.compositor_api.send_display_list(
|
||||||
.send_display_list(compositor_info, builder.end().1);
|
self.webview_id,
|
||||||
|
compositor_info,
|
||||||
|
builder.end().1,
|
||||||
|
);
|
||||||
|
|
||||||
let (keys, instance_keys) = self
|
let (keys, instance_keys) = self
|
||||||
.font_context
|
.font_context
|
||||||
|
@ -1040,6 +1043,7 @@ impl LayoutThread {
|
||||||
|
|
||||||
let point = Point2D::new(-state.scroll_offset.x, -state.scroll_offset.y);
|
let point = Point2D::new(-state.scroll_offset.x, -state.scroll_offset.y);
|
||||||
self.compositor_api.send_scroll_node(
|
self.compositor_api.send_scroll_node(
|
||||||
|
self.webview_id,
|
||||||
self.id.into(),
|
self.id.into(),
|
||||||
units::LayoutPoint::from_untyped(point),
|
units::LayoutPoint::from_untyped(point),
|
||||||
state.scroll_id,
|
state.scroll_id,
|
||||||
|
|
|
@ -820,6 +820,7 @@ impl LayoutThread {
|
||||||
.insert(state.scroll_id, state.scroll_offset);
|
.insert(state.scroll_id, state.scroll_offset);
|
||||||
let point = Point2D::new(-state.scroll_offset.x, -state.scroll_offset.y);
|
let point = Point2D::new(-state.scroll_offset.x, -state.scroll_offset.y);
|
||||||
self.compositor_api.send_scroll_node(
|
self.compositor_api.send_scroll_node(
|
||||||
|
self.webview_id,
|
||||||
self.id.into(),
|
self.id.into(),
|
||||||
units::LayoutPoint::from_untyped(point),
|
units::LayoutPoint::from_untyped(point),
|
||||||
state.scroll_id,
|
state.scroll_id,
|
||||||
|
@ -890,8 +891,11 @@ impl LayoutThread {
|
||||||
.maybe_observe_paint_time(self, epoch, is_contentful);
|
.maybe_observe_paint_time(self, epoch, is_contentful);
|
||||||
|
|
||||||
if reflow_goal.needs_display() {
|
if reflow_goal.needs_display() {
|
||||||
self.compositor_api
|
self.compositor_api.send_display_list(
|
||||||
.send_display_list(display_list.compositor_info, display_list.wr.end().1);
|
self.webview_id,
|
||||||
|
display_list.compositor_info,
|
||||||
|
display_list.wr.end().1,
|
||||||
|
);
|
||||||
|
|
||||||
let (keys, instance_keys) = self
|
let (keys, instance_keys) = self
|
||||||
.font_context
|
.font_context
|
||||||
|
|
|
@ -9,7 +9,7 @@ use std::time::Duration;
|
||||||
|
|
||||||
use base::Epoch;
|
use base::Epoch;
|
||||||
use base::cross_process_instant::CrossProcessInstant;
|
use base::cross_process_instant::CrossProcessInstant;
|
||||||
use base::id::PipelineId;
|
use base::id::{PipelineId, WebViewId};
|
||||||
use ipc_channel::ipc::IpcSender;
|
use ipc_channel::ipc::IpcSender;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use malloc_size_of_derive::MallocSizeOf;
|
use malloc_size_of_derive::MallocSizeOf;
|
||||||
|
@ -255,6 +255,7 @@ pub struct PaintTimeMetrics {
|
||||||
navigation_start: CrossProcessInstant,
|
navigation_start: CrossProcessInstant,
|
||||||
first_paint: Cell<Option<CrossProcessInstant>>,
|
first_paint: Cell<Option<CrossProcessInstant>>,
|
||||||
first_contentful_paint: Cell<Option<CrossProcessInstant>>,
|
first_contentful_paint: Cell<Option<CrossProcessInstant>>,
|
||||||
|
webview_id: WebViewId,
|
||||||
pipeline_id: PipelineId,
|
pipeline_id: PipelineId,
|
||||||
time_profiler_chan: ProfilerChan,
|
time_profiler_chan: ProfilerChan,
|
||||||
constellation_chan: IpcSender<LayoutMsg>,
|
constellation_chan: IpcSender<LayoutMsg>,
|
||||||
|
@ -264,6 +265,7 @@ pub struct PaintTimeMetrics {
|
||||||
|
|
||||||
impl PaintTimeMetrics {
|
impl PaintTimeMetrics {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
webview_id: WebViewId,
|
||||||
pipeline_id: PipelineId,
|
pipeline_id: PipelineId,
|
||||||
time_profiler_chan: ProfilerChan,
|
time_profiler_chan: ProfilerChan,
|
||||||
constellation_chan: IpcSender<LayoutMsg>,
|
constellation_chan: IpcSender<LayoutMsg>,
|
||||||
|
@ -276,6 +278,7 @@ impl PaintTimeMetrics {
|
||||||
navigation_start,
|
navigation_start,
|
||||||
first_paint: Cell::new(None),
|
first_paint: Cell::new(None),
|
||||||
first_contentful_paint: Cell::new(None),
|
first_contentful_paint: Cell::new(None),
|
||||||
|
webview_id,
|
||||||
pipeline_id,
|
pipeline_id,
|
||||||
time_profiler_chan,
|
time_profiler_chan,
|
||||||
constellation_chan,
|
constellation_chan,
|
||||||
|
@ -308,7 +311,7 @@ impl PaintTimeMetrics {
|
||||||
// Send the pending metric information to the compositor thread.
|
// Send the pending metric information to the compositor thread.
|
||||||
// The compositor will record the current time after painting the
|
// The compositor will record the current time after painting the
|
||||||
// frame with the given ID and will send the metric back to us.
|
// 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) {
|
if let Err(e) = self.constellation_chan.send(msg) {
|
||||||
warn!("Failed to send PendingPaintMetric {:?}", e);
|
warn!("Failed to send PendingPaintMetric {:?}", e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3051,6 +3051,7 @@ impl ScriptThread {
|
||||||
};
|
};
|
||||||
|
|
||||||
let paint_time_metrics = PaintTimeMetrics::new(
|
let paint_time_metrics = PaintTimeMetrics::new(
|
||||||
|
incomplete.top_level_browsing_context_id,
|
||||||
incomplete.pipeline_id,
|
incomplete.pipeline_id,
|
||||||
self.senders.time_profiler_sender.clone(),
|
self.senders.time_profiler_sender.clone(),
|
||||||
self.senders.layout_to_constellation_ipc_sender.clone(),
|
self.senders.layout_to_constellation_ipc_sender.clone(),
|
||||||
|
|
|
@ -9,7 +9,7 @@ mod constellation_msg;
|
||||||
use std::fmt::{Debug, Error, Formatter};
|
use std::fmt::{Debug, Error, Formatter};
|
||||||
|
|
||||||
use base::Epoch;
|
use base::Epoch;
|
||||||
use base::id::{PipelineId, TopLevelBrowsingContextId};
|
use base::id::{PipelineId, TopLevelBrowsingContextId, WebViewId};
|
||||||
pub use constellation_msg::ConstellationMsg;
|
pub use constellation_msg::ConstellationMsg;
|
||||||
use crossbeam_channel::{Receiver, Sender};
|
use crossbeam_channel::{Receiver, Sender};
|
||||||
use embedder_traits::{EventLoopWaker, MouseButton, MouseButtonAction};
|
use embedder_traits::{EventLoopWaker, MouseButton, MouseButtonAction};
|
||||||
|
@ -59,7 +59,7 @@ impl CompositorReceiver {
|
||||||
/// Messages from (or via) the constellation thread to the compositor.
|
/// Messages from (or via) the constellation thread to the compositor.
|
||||||
pub enum CompositorMsg {
|
pub enum CompositorMsg {
|
||||||
/// Alerts the compositor that the given pipeline has changed whether it is running animations.
|
/// 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.
|
/// Create or update a webview, given its frame tree.
|
||||||
CreateOrUpdateWebView(SendableFrameTree),
|
CreateOrUpdateWebView(SendableFrameTree),
|
||||||
/// Remove a webview.
|
/// Remove a webview.
|
||||||
|
@ -71,7 +71,7 @@ pub enum CompositorMsg {
|
||||||
/// A reply to the compositor asking if the output image is stable.
|
/// A reply to the compositor asking if the output image is stable.
|
||||||
IsReadyToSaveImageReply(bool),
|
IsReadyToSaveImageReply(bool),
|
||||||
/// Set whether to use less resources by stopping animations.
|
/// 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
|
/// 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
|
/// the frame is ready. It contains a bool to indicate if it needs to composite and the
|
||||||
/// `DocumentId` of the new frame.
|
/// `DocumentId` of the new frame.
|
||||||
|
@ -81,11 +81,11 @@ pub enum CompositorMsg {
|
||||||
// when it shuts down a pipeline, to the compositor; when the compositor
|
// 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
|
// sends a reply on the IpcSender, the constellation knows it's safe to
|
||||||
// tear down the other threads associated with this pipeline.
|
// 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
|
/// 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
|
/// the given ID (epoch) is painted and report it to the layout of the given
|
||||||
/// pipeline ID.
|
/// WebViewId and PipelienId.
|
||||||
PendingPaintMetric(PipelineId, Epoch),
|
PendingPaintMetric(WebViewId, PipelineId, Epoch),
|
||||||
/// The load of a page has completed
|
/// The load of a page has completed
|
||||||
LoadComplete(TopLevelBrowsingContextId),
|
LoadComplete(TopLevelBrowsingContextId),
|
||||||
/// WebDriver mouse button event
|
/// WebDriver mouse button event
|
||||||
|
@ -114,7 +114,7 @@ pub struct CompositionPipeline {
|
||||||
impl Debug for CompositorMsg {
|
impl Debug for CompositorMsg {
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
|
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
|
||||||
match *self {
|
match *self {
|
||||||
CompositorMsg::ChangeRunningAnimationsState(_, state) => {
|
CompositorMsg::ChangeRunningAnimationsState(_, _, state) => {
|
||||||
write!(f, "ChangeRunningAnimationsState({:?})", state)
|
write!(f, "ChangeRunningAnimationsState({:?})", state)
|
||||||
},
|
},
|
||||||
CompositorMsg::CreateOrUpdateWebView(..) => write!(f, "CreateOrUpdateWebView"),
|
CompositorMsg::CreateOrUpdateWebView(..) => write!(f, "CreateOrUpdateWebView"),
|
||||||
|
|
|
@ -9,7 +9,7 @@ use base::Epoch;
|
||||||
use base::id::{
|
use base::id::{
|
||||||
BroadcastChannelRouterId, BrowsingContextId, HistoryStateId, MessagePortId,
|
BroadcastChannelRouterId, BrowsingContextId, HistoryStateId, MessagePortId,
|
||||||
MessagePortRouterId, PipelineId, ServiceWorkerId, ServiceWorkerRegistrationId,
|
MessagePortRouterId, PipelineId, ServiceWorkerId, ServiceWorkerRegistrationId,
|
||||||
TopLevelBrowsingContextId,
|
TopLevelBrowsingContextId, WebViewId,
|
||||||
};
|
};
|
||||||
use canvas_traits::canvas::{CanvasId, CanvasMsg};
|
use canvas_traits::canvas::{CanvasId, CanvasMsg};
|
||||||
use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
|
use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
|
||||||
|
@ -49,7 +49,7 @@ pub struct IFrameSizeMsg {
|
||||||
pub enum LayoutMsg {
|
pub enum LayoutMsg {
|
||||||
/// Requests that the constellation inform the compositor that it needs to record
|
/// Requests that the constellation inform the compositor that it needs to record
|
||||||
/// the time when the frame with the given ID (epoch) is painted.
|
/// the time when the frame with the given ID (epoch) is painted.
|
||||||
PendingPaintMetric(PipelineId, Epoch),
|
PendingPaintMetric(WebViewId, PipelineId, Epoch),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for LayoutMsg {
|
impl fmt::Debug for LayoutMsg {
|
||||||
|
|
|
@ -11,7 +11,7 @@ use core::fmt;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use base::id::PipelineId;
|
use base::id::{PipelineId, WebViewId};
|
||||||
use display_list::{CompositorDisplayListInfo, ScrollTreeNodeId};
|
use display_list::{CompositorDisplayListInfo, ScrollTreeNodeId};
|
||||||
use embedder_traits::Cursor;
|
use embedder_traits::Cursor;
|
||||||
use euclid::default::Size2D as UntypedSize2D;
|
use euclid::default::Size2D as UntypedSize2D;
|
||||||
|
@ -33,9 +33,16 @@ pub enum CrossProcessCompositorMessage {
|
||||||
/// Inform WebRender of the existence of this pipeline.
|
/// Inform WebRender of the existence of this pipeline.
|
||||||
SendInitialTransaction(WebRenderPipelineId),
|
SendInitialTransaction(WebRenderPipelineId),
|
||||||
/// Perform a scroll operation.
|
/// Perform a scroll operation.
|
||||||
SendScrollNode(WebRenderPipelineId, LayoutPoint, ExternalScrollId),
|
SendScrollNode(
|
||||||
|
WebViewId,
|
||||||
|
WebRenderPipelineId,
|
||||||
|
LayoutPoint,
|
||||||
|
ExternalScrollId,
|
||||||
|
),
|
||||||
/// Inform WebRender of a new display list for the given pipeline.
|
/// Inform WebRender of a new display list for the given pipeline.
|
||||||
SendDisplayList {
|
SendDisplayList {
|
||||||
|
/// The [`WebViewId`] that this display list belongs to.
|
||||||
|
webview_id: WebViewId,
|
||||||
/// The [CompositorDisplayListInfo] that describes the display list being sent.
|
/// The [CompositorDisplayListInfo] that describes the display list being sent.
|
||||||
display_list_info: Box<CompositorDisplayListInfo>,
|
display_list_info: Box<CompositorDisplayListInfo>,
|
||||||
/// A descriptor of this display list used to construct this display list from raw data.
|
/// 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.
|
/// Perform a scroll operation.
|
||||||
pub fn send_scroll_node(
|
pub fn send_scroll_node(
|
||||||
&self,
|
&self,
|
||||||
|
webview_id: WebViewId,
|
||||||
pipeline_id: WebRenderPipelineId,
|
pipeline_id: WebRenderPipelineId,
|
||||||
point: LayoutPoint,
|
point: LayoutPoint,
|
||||||
scroll_id: ExternalScrollId,
|
scroll_id: ExternalScrollId,
|
||||||
) {
|
) {
|
||||||
if let Err(e) = self.0.send(CrossProcessCompositorMessage::SendScrollNode(
|
if let Err(e) = self.0.send(CrossProcessCompositorMessage::SendScrollNode(
|
||||||
|
webview_id,
|
||||||
pipeline_id,
|
pipeline_id,
|
||||||
point,
|
point,
|
||||||
scroll_id,
|
scroll_id,
|
||||||
|
@ -154,12 +163,14 @@ impl CrossProcessCompositorApi {
|
||||||
/// Inform WebRender of a new display list for the given pipeline.
|
/// Inform WebRender of a new display list for the given pipeline.
|
||||||
pub fn send_display_list(
|
pub fn send_display_list(
|
||||||
&self,
|
&self,
|
||||||
|
webview_id: WebViewId,
|
||||||
display_list_info: CompositorDisplayListInfo,
|
display_list_info: CompositorDisplayListInfo,
|
||||||
list: BuiltDisplayList,
|
list: BuiltDisplayList,
|
||||||
) {
|
) {
|
||||||
let (display_list_data, display_list_descriptor) = list.into_data();
|
let (display_list_data, display_list_descriptor) = list.into_data();
|
||||||
let (display_list_sender, display_list_receiver) = ipc::bytes_channel().unwrap();
|
let (display_list_sender, display_list_receiver) = ipc::bytes_channel().unwrap();
|
||||||
if let Err(e) = self.0.send(CrossProcessCompositorMessage::SendDisplayList {
|
if let Err(e) = self.0.send(CrossProcessCompositorMessage::SendDisplayList {
|
||||||
|
webview_id,
|
||||||
display_list_info: Box::new(display_list_info),
|
display_list_info: Box::new(display_list_info),
|
||||||
display_list_descriptor,
|
display_list_descriptor,
|
||||||
display_list_receiver,
|
display_list_receiver,
|
||||||
|
|
|
@ -431,14 +431,8 @@ impl Handler {
|
||||||
target_y: i64,
|
target_y: i64,
|
||||||
tick_start: Instant,
|
tick_start: Instant,
|
||||||
) {
|
) {
|
||||||
let pointer_input_state = match self
|
let session = self.session.as_mut().unwrap();
|
||||||
.session
|
let pointer_input_state = match session.input_state_table.get_mut(source_id).unwrap() {
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.input_state_table
|
|
||||||
.get_mut(source_id)
|
|
||||||
.unwrap()
|
|
||||||
{
|
|
||||||
InputSourceState::Null => unreachable!(),
|
InputSourceState::Null => unreachable!(),
|
||||||
InputSourceState::Key(_) => unreachable!(),
|
InputSourceState::Key(_) => unreachable!(),
|
||||||
InputSourceState::Pointer(pointer_input_state) => pointer_input_state,
|
InputSourceState::Pointer(pointer_input_state) => pointer_input_state,
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use base::Epoch;
|
use base::Epoch;
|
||||||
use base::cross_process_instant::CrossProcessInstant;
|
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 ipc_channel::ipc;
|
||||||
use metrics::{PaintTimeMetrics, ProfilerMetadataFactory, ProgressiveWebMetric};
|
use metrics::{PaintTimeMetrics, ProfilerMetadataFactory, ProgressiveWebMetric};
|
||||||
use profile_traits::time::{ProfilerChan, TimerMetadata};
|
use profile_traits::time::{ProfilerChan, TimerMetadata};
|
||||||
|
@ -25,6 +25,7 @@ fn test_paint_metrics_construction() {
|
||||||
let (script_sender, _) = ipc::channel().unwrap();
|
let (script_sender, _) = ipc::channel().unwrap();
|
||||||
let start_time = CrossProcessInstant::now();
|
let start_time = CrossProcessInstant::now();
|
||||||
let paint_time_metrics = PaintTimeMetrics::new(
|
let paint_time_metrics = PaintTimeMetrics::new(
|
||||||
|
TEST_WEBVIEW_ID,
|
||||||
TEST_PIPELINE_ID,
|
TEST_PIPELINE_ID,
|
||||||
profiler_chan,
|
profiler_chan,
|
||||||
layout_sender,
|
layout_sender,
|
||||||
|
@ -56,6 +57,7 @@ fn test_common(display_list_is_contentful: bool, epoch: Epoch) -> PaintTimeMetri
|
||||||
let (script_sender, _) = ipc::channel().unwrap();
|
let (script_sender, _) = ipc::channel().unwrap();
|
||||||
let start_time = CrossProcessInstant::now();
|
let start_time = CrossProcessInstant::now();
|
||||||
let mut paint_time_metrics = PaintTimeMetrics::new(
|
let mut paint_time_metrics = PaintTimeMetrics::new(
|
||||||
|
TEST_WEBVIEW_ID,
|
||||||
TEST_PIPELINE_ID,
|
TEST_PIPELINE_ID,
|
||||||
profiler_chan,
|
profiler_chan,
|
||||||
layout_sender,
|
layout_sender,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue