aboutsummaryrefslogtreecommitdiffstats
path: root/components/compositing/webview_renderer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/compositing/webview_renderer.rs')
-rw-r--r--components/compositing/webview_renderer.rs168
1 files changed, 115 insertions, 53 deletions
diff --git a/components/compositing/webview_renderer.rs b/components/compositing/webview_renderer.rs
index b0e91ccb02e..056ffc16b89 100644
--- a/components/compositing/webview_renderer.rs
+++ b/components/compositing/webview_renderer.rs
@@ -2,12 +2,15 @@
* 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::cell::{Cell, RefCell};
use std::collections::hash_map::Keys;
+use std::collections::{HashMap, VecDeque};
use std::rc::Rc;
use base::id::{PipelineId, WebViewId};
+use compositing_traits::viewport_description::{
+ DEFAULT_ZOOM, MAX_ZOOM, MIN_ZOOM, ViewportDescription,
+};
use compositing_traits::{SendableFrameTree, WebViewTrait};
use constellation_traits::{EmbedderToConstellationMessage, ScrollState, WindowSizeType};
use embedder_traits::{
@@ -25,13 +28,9 @@ use webrender_api::units::{
};
use webrender_api::{ExternalScrollId, HitTestFlags, ScrollLocation};
-use crate::compositor::{PipelineDetails, ServoRenderer};
+use crate::compositor::{HitTestError, PipelineDetails, ServoRenderer};
use crate::touch::{TouchHandler, TouchMoveAction, TouchMoveAllowed, TouchSequenceState};
-// Default viewport constraints
-const MAX_ZOOM: f32 = 8.0;
-const MIN_ZOOM: f32 = 0.1;
-
#[derive(Clone, Copy)]
struct ScrollEvent {
/// Scroll by this offset, or to Start or End
@@ -44,8 +43,10 @@ struct ScrollEvent {
#[derive(Clone, Copy)]
enum ScrollZoomEvent {
- /// An pinch zoom event that magnifies the view by the given factor.
+ /// A pinch zoom event that magnifies the view by the given factor.
PinchZoom(f32),
+ /// A zoom event that magnifies the view by the factor parsed from meta tag.
+ ViewportZoom(f32),
/// A scroll event that scrolls the scroll node at the given location by the
/// given amount.
Scroll(ScrollEvent),
@@ -58,7 +59,7 @@ pub(crate) struct ScrollResult {
pub offset: LayoutVector2D,
}
-#[derive(PartialEq)]
+#[derive(Debug, PartialEq)]
pub(crate) enum PinchZoomResult {
DidPinchZoom,
DidNotPinchZoom,
@@ -89,15 +90,18 @@ pub(crate) struct WebViewRenderer {
pub page_zoom: Scale<f32, CSSPixel, DeviceIndependentPixel>,
/// "Mobile-style" zoom that does not reflow the page.
viewport_zoom: PinchZoomFactor,
- /// Viewport zoom constraints provided by @viewport.
- min_viewport_zoom: Option<PinchZoomFactor>,
- max_viewport_zoom: Option<PinchZoomFactor>,
/// The HiDPI scale factor for the `WebView` associated with this renderer. This is controlled
/// by the embedding layer.
hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
/// Whether or not this [`WebViewRenderer`] isn't throttled and has a pipeline with
/// active animations or animation frame callbacks.
animating: bool,
+ /// Pending input events queue. Priavte and only this thread pushes events to it.
+ pending_point_input_events: RefCell<VecDeque<InputEvent>>,
+ /// WebRender is not ready between `SendDisplayList` and `WebRenderFrameReady` messages.
+ pub webrender_frame_ready: Cell<bool>,
+ /// Viewport Description
+ viewport_description: Option<ViewportDescription>,
}
impl Drop for WebViewRenderer {
@@ -127,11 +131,12 @@ impl WebViewRenderer {
global,
pending_scroll_zoom_events: Default::default(),
page_zoom: Scale::new(1.0),
- viewport_zoom: PinchZoomFactor::new(1.0),
- min_viewport_zoom: Some(PinchZoomFactor::new(1.0)),
- max_viewport_zoom: None,
+ viewport_zoom: PinchZoomFactor::new(DEFAULT_ZOOM),
hidpi_scale_factor: Scale::new(hidpi_scale_factor.0),
animating: false,
+ pending_point_input_events: Default::default(),
+ webrender_frame_ready: Cell::default(),
+ viewport_description: None,
}
}
@@ -309,29 +314,89 @@ impl WebViewRenderer {
}
}
- pub(crate) fn dispatch_input_event(&mut self, event: InputEvent) {
+ pub(crate) fn dispatch_point_input_event(&mut self, mut event: InputEvent) -> bool {
// Events that do not need to do hit testing are sent directly to the
// constellation to filter down.
let Some(point) = event.point() else {
- return;
+ return false;
};
+ // Delay the event if the epoch is not synchronized yet (new frame is not ready),
+ // or hit test result would fail and the event is rejected anyway.
+ if !self.webrender_frame_ready.get() || !self.pending_point_input_events.borrow().is_empty()
+ {
+ self.pending_point_input_events
+ .borrow_mut()
+ .push_back(event);
+ return false;
+ }
+
// If we can't find a pipeline to send this event to, we cannot continue.
let get_pipeline_details = |pipeline_id| self.pipelines.get(&pipeline_id);
- let Some(result) = self
+ let result = match self
.global
.borrow()
.hit_test_at_point(point, get_pipeline_details)
- else {
- return;
+ {
+ Ok(hit_test_results) => hit_test_results,
+ Err(HitTestError::EpochMismatch) => {
+ self.pending_point_input_events
+ .borrow_mut()
+ .push_back(event);
+ return false;
+ },
+ _ => {
+ return false;
+ },
};
- self.global.borrow_mut().update_cursor(point, &result);
+ match event {
+ InputEvent::Touch(ref mut touch_event) => {
+ touch_event.init_sequence_id(self.touch_handler.current_sequence_id);
+ },
+ InputEvent::MouseButton(_) | InputEvent::MouseMove(_) | InputEvent::Wheel(_) => {
+ self.global.borrow_mut().update_cursor(point, &result);
+ },
+ _ => unreachable!("Unexpected input event type: {event:?}"),
+ }
if let Err(error) = self.global.borrow().constellation_sender.send(
EmbedderToConstellationMessage::ForwardInputEvent(self.id, event, Some(result)),
) {
warn!("Sending event to constellation failed ({error:?}).");
+ false
+ } else {
+ true
+ }
+ }
+
+ pub(crate) fn dispatch_pending_point_input_events(&self) {
+ while let Some(event) = self.pending_point_input_events.borrow_mut().pop_front() {
+ // Events that do not need to do hit testing are sent directly to the
+ // constellation to filter down.
+ let Some(point) = event.point() else {
+ continue;
+ };
+
+ // If we can't find a pipeline to send this event to, we cannot continue.
+ let get_pipeline_details = |pipeline_id| self.pipelines.get(&pipeline_id);
+ let Ok(result) = self
+ .global
+ .borrow()
+ .hit_test_at_point(point, get_pipeline_details)
+ else {
+ // Don't need to process pending input events in this frame any more.
+ // TODO: Add multiple retry later if needed.
+ return;
+ };
+
+ self.global.borrow_mut().update_cursor(point, &result);
+
+ if let Err(error) = self.global.borrow().constellation_sender.send(
+ EmbedderToConstellationMessage::ForwardInputEvent(self.id, event, Some(result)),
+ ) {
+ warn!("Sending event to constellation failed ({error:?}).");
+ }
}
}
@@ -401,29 +466,11 @@ impl WebViewRenderer {
}
}
- self.dispatch_input_event(event);
+ self.dispatch_point_input_event(event);
}
- fn send_touch_event(&self, mut event: TouchEvent) -> bool {
- let get_pipeline_details = |pipeline_id| self.pipelines.get(&pipeline_id);
- let Some(result) = self
- .global
- .borrow()
- .hit_test_at_point(event.point, get_pipeline_details)
- else {
- return false;
- };
-
- event.init_sequence_id(self.touch_handler.current_sequence_id);
- let event = InputEvent::Touch(event);
- if let Err(e) = self.global.borrow().constellation_sender.send(
- EmbedderToConstellationMessage::ForwardInputEvent(self.id, event, Some(result)),
- ) {
- warn!("Sending event to constellation failed ({:?}).", e);
- false
- } else {
- true
- }
+ fn send_touch_event(&mut self, event: TouchEvent) -> bool {
+ self.dispatch_point_input_event(InputEvent::Touch(event))
}
pub(crate) fn on_touch_event(&mut self, event: TouchEvent) {
@@ -687,13 +734,13 @@ impl WebViewRenderer {
/// <http://w3c.github.io/touch-events/#mouse-events>
fn simulate_mouse_click(&mut self, point: DevicePoint) {
let button = MouseButton::Left;
- self.dispatch_input_event(InputEvent::MouseMove(MouseMoveEvent::new(point)));
- self.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent::new(
+ self.dispatch_point_input_event(InputEvent::MouseMove(MouseMoveEvent::new(point)));
+ self.dispatch_point_input_event(InputEvent::MouseButton(MouseButtonEvent::new(
MouseButtonAction::Down,
button,
point,
)));
- self.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent::new(
+ self.dispatch_point_input_event(InputEvent::MouseButton(MouseButtonEvent::new(
MouseButtonAction::Up,
button,
point,
@@ -764,7 +811,8 @@ impl WebViewRenderer {
let mut combined_magnification = 1.0;
for scroll_event in self.pending_scroll_zoom_events.drain(..) {
match scroll_event {
- ScrollZoomEvent::PinchZoom(magnification) => {
+ ScrollZoomEvent::PinchZoom(magnification) |
+ ScrollZoomEvent::ViewportZoom(magnification) => {
combined_magnification *= magnification
},
ScrollZoomEvent::Scroll(scroll_event_info) => {
@@ -858,7 +906,8 @@ impl WebViewRenderer {
HitTestFlags::FIND_ALL,
None,
get_pipeline_details,
- );
+ )
+ .unwrap_or_default();
// Iterate through all hit test results, processing only the first node of each pipeline.
// This is needed to propagate the scroll events from a pipeline representing an iframe to
@@ -892,11 +941,8 @@ impl WebViewRenderer {
}
fn set_pinch_zoom_level(&mut self, mut zoom: f32) -> bool {
- if let Some(min) = self.min_viewport_zoom {
- zoom = f32::max(min.get(), zoom);
- }
- if let Some(max) = self.max_viewport_zoom {
- zoom = f32::min(max.get(), zoom);
+ if let Some(viewport) = self.viewport_description.as_ref() {
+ zoom = viewport.clamp_zoom(zoom);
}
let old_zoom = std::mem::replace(&mut self.viewport_zoom, PinchZoomFactor::new(zoom));
@@ -926,7 +972,12 @@ impl WebViewRenderer {
// TODO: Scroll to keep the center in view?
self.pending_scroll_zoom_events
- .push(ScrollZoomEvent::PinchZoom(magnification));
+ .push(ScrollZoomEvent::PinchZoom(
+ self.viewport_description
+ .clone()
+ .unwrap_or_default()
+ .clamp_zoom(magnification),
+ ));
}
fn send_window_size_message(&self) {
@@ -993,6 +1044,17 @@ impl WebViewRenderer {
let screen_geometry = self.webview.screen_geometry().unwrap_or_default();
(screen_geometry.available_size.to_f32() / self.hidpi_scale_factor).to_i32()
}
+
+ pub fn set_viewport_description(&mut self, viewport_description: ViewportDescription) {
+ self.pending_scroll_zoom_events
+ .push(ScrollZoomEvent::ViewportZoom(
+ self.viewport_description
+ .clone()
+ .unwrap_or_default()
+ .clamp_zoom(viewport_description.initial_scale.get()),
+ ));
+ self.viewport_description = Some(viewport_description);
+ }
}
#[derive(Clone, Copy, Debug, PartialEq)]