1use std::collections::hash_map::Entry;
6use std::rc::Rc;
7
8use base::id::{PipelineId, WebViewId};
9use compositing_traits::display_list::ScrollType;
10use compositing_traits::viewport_description::{
11 DEFAULT_PAGE_ZOOM, MAX_PAGE_ZOOM, MIN_PAGE_ZOOM, ViewportDescription,
12};
13use compositing_traits::{PipelineExitSource, SendableFrameTree, WebViewTrait};
14use constellation_traits::{EmbedderToConstellationMessage, WindowSizeType};
15use crossbeam_channel::Sender;
16use embedder_traits::{
17 AnimationState, CompositorHitTestResult, InputEvent, InputEventAndId, InputEventId,
18 InputEventResult, MouseButton, MouseButtonAction, MouseButtonEvent, MouseMoveEvent, Scroll,
19 ScrollEvent as EmbedderScrollEvent, TouchEvent, TouchEventType, ViewportDetails, WebViewPoint,
20 WheelEvent,
21};
22use euclid::{Scale, Vector2D};
23use log::{debug, warn};
24use malloc_size_of::MallocSizeOf;
25use rustc_hash::FxHashMap;
26use servo_geometry::DeviceIndependentPixel;
27use style_traits::CSSPixel;
28use webrender::RenderApi;
29use webrender_api::units::{DevicePixel, DevicePoint, DeviceRect, DeviceVector2D, LayoutVector2D};
30use webrender_api::{DocumentId, ExternalScrollId, ScrollLocation};
31
32use crate::painter::Painter;
33use crate::pinch_zoom::PinchZoom;
34use crate::pipeline_details::PipelineDetails;
35use crate::refresh_driver::BaseRefreshDriver;
36use crate::touch::{
37 FlingRefreshDriverObserver, PendingTouchInputEvent, TouchHandler, TouchMoveAllowed,
38 TouchSequenceState,
39};
40
41#[derive(Clone, Copy)]
42pub(crate) struct ScrollEvent {
43 pub scroll: Scroll,
45 pub point: DevicePoint,
47 pub event_count: u32,
49}
50
51#[derive(Clone, Copy)]
52pub(crate) enum ScrollZoomEvent {
53 PinchZoom(f32, DevicePoint),
56 Scroll(ScrollEvent),
59}
60
61#[derive(Clone, Debug)]
62pub(crate) struct ScrollResult {
63 pub hit_test_result: CompositorHitTestResult,
64 pub external_scroll_id: ExternalScrollId,
65 pub offset: LayoutVector2D,
66}
67
68#[derive(Debug, PartialEq)]
69pub(crate) enum PinchZoomResult {
70 DidPinchZoom,
71 DidNotPinchZoom,
72}
73
74pub(crate) struct WebViewRenderer {
78 pub id: WebViewId,
80 pub webview: Box<dyn WebViewTrait>,
84 pub root_pipeline_id: Option<PipelineId>,
86 pub rect: DeviceRect,
88 pub pipelines: FxHashMap<PipelineId, PipelineDetails>,
90 pending_scroll_zoom_events: Vec<ScrollZoomEvent>,
92 touch_handler: TouchHandler,
94 pub page_zoom: Scale<f32, CSSPixel, DeviceIndependentPixel>,
96 pinch_zoom: PinchZoom,
99 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
102 animating: bool,
105 viewport_description: Option<ViewportDescription>,
108
109 embedder_to_constellation_sender: Sender<EmbedderToConstellationMessage>,
114 refresh_driver: Rc<BaseRefreshDriver>,
116 webrender_document: DocumentId,
118}
119
120impl WebViewRenderer {
121 pub(crate) fn new(
122 renderer_webview: Box<dyn WebViewTrait>,
123 viewport_details: ViewportDetails,
124 embedder_to_constellation_sender: Sender<EmbedderToConstellationMessage>,
125 refresh_driver: Rc<BaseRefreshDriver>,
126 webrender_document: DocumentId,
127 ) -> Self {
128 let hidpi_scale_factor = viewport_details.hidpi_scale_factor;
129 let size = viewport_details.size * viewport_details.hidpi_scale_factor;
130 let rect = DeviceRect::from_origin_and_size(DevicePoint::origin(), size);
131 Self {
132 id: renderer_webview.id(),
133 webview: renderer_webview,
134 root_pipeline_id: None,
135 rect,
136 pipelines: Default::default(),
137 touch_handler: TouchHandler::new(),
138 pending_scroll_zoom_events: Default::default(),
139 page_zoom: DEFAULT_PAGE_ZOOM,
140 pinch_zoom: PinchZoom::new(rect),
141 hidpi_scale_factor: Scale::new(hidpi_scale_factor.0),
142 animating: false,
143 viewport_description: None,
144 embedder_to_constellation_sender,
145 refresh_driver,
146 webrender_document,
147 }
148 }
149
150 fn hit_test(
151 &self,
152 webrender_api: &RenderApi,
153 point: DevicePoint,
154 ) -> Vec<CompositorHitTestResult> {
155 Painter::hit_test_at_point_with_api_and_document(
156 webrender_api,
157 self.webrender_document,
158 point,
159 )
160 }
161
162 pub(crate) fn animation_callbacks_running(&self) -> bool {
163 self.pipelines
164 .values()
165 .any(PipelineDetails::animation_callbacks_running)
166 }
167
168 pub(crate) fn animating(&self) -> bool {
169 self.animating
170 }
171
172 pub(crate) fn ensure_pipeline_details(
174 &mut self,
175 pipeline_id: PipelineId,
176 ) -> &mut PipelineDetails {
177 self.pipelines
178 .entry(pipeline_id)
179 .or_insert_with(PipelineDetails::new)
180 }
181
182 pub(crate) fn pipeline_exited(&mut self, pipeline_id: PipelineId, source: PipelineExitSource) {
183 let pipeline = self.pipelines.entry(pipeline_id);
184 let Entry::Occupied(mut pipeline) = pipeline else {
185 return;
186 };
187
188 pipeline.get_mut().exited.insert(source);
189
190 if !pipeline.get().exited.is_all() {
194 return;
195 }
196
197 pipeline.remove_entry();
198 }
199
200 pub(crate) fn set_frame_tree(&mut self, frame_tree: &SendableFrameTree) {
201 let pipeline_id = frame_tree.pipeline.id;
202 let old_pipeline_id = self.root_pipeline_id.replace(pipeline_id);
203
204 if old_pipeline_id != self.root_pipeline_id {
205 debug!(
206 "Updating webview ({:?}) from pipeline {:?} to {:?}",
207 3, old_pipeline_id, self.root_pipeline_id
208 );
209 }
210
211 self.set_frame_tree_on_pipeline_details(frame_tree, None);
212 }
213
214 pub(crate) fn send_scroll_positions_to_layout_for_pipeline(&self, pipeline_id: PipelineId) {
215 let Some(details) = self.pipelines.get(&pipeline_id) else {
216 return;
217 };
218
219 let scroll_offsets = details.scroll_tree.scroll_offsets();
220
221 if scroll_offsets.is_empty() {
226 return;
227 }
228
229 let _ = self.embedder_to_constellation_sender.send(
230 EmbedderToConstellationMessage::SetScrollStates(pipeline_id, scroll_offsets),
231 );
232 }
233
234 pub(crate) fn set_frame_tree_on_pipeline_details(
235 &mut self,
236 frame_tree: &SendableFrameTree,
237 parent_pipeline_id: Option<PipelineId>,
238 ) {
239 let pipeline_id = frame_tree.pipeline.id;
240 let pipeline_details = self.ensure_pipeline_details(pipeline_id);
241 pipeline_details.pipeline = Some(frame_tree.pipeline.clone());
242 pipeline_details.parent_pipeline_id = parent_pipeline_id;
243
244 for kid in &frame_tree.children {
245 self.set_frame_tree_on_pipeline_details(kid, Some(pipeline_id));
246 }
247 }
248
249 pub(crate) fn change_pipeline_running_animations_state(
252 &mut self,
253 pipeline_id: PipelineId,
254 animation_state: AnimationState,
255 ) -> bool {
256 let pipeline_details = self.ensure_pipeline_details(pipeline_id);
257 let was_animating = pipeline_details.animating();
258 match animation_state {
259 AnimationState::AnimationsPresent => {
260 pipeline_details.animations_running = true;
261 },
262 AnimationState::AnimationCallbacksPresent => {
263 pipeline_details.animation_callbacks_running = true;
264 },
265 AnimationState::NoAnimationsPresent => {
266 pipeline_details.animations_running = false;
267 },
268 AnimationState::NoAnimationCallbacksPresent => {
269 pipeline_details.animation_callbacks_running = false;
270 },
271 }
272 let started_animating = !was_animating && pipeline_details.animating();
273
274 self.update_animation_state();
275
276 started_animating
281 }
282
283 pub(crate) fn set_throttled(&mut self, pipeline_id: PipelineId, throttled: bool) -> bool {
286 let pipeline_details = self.ensure_pipeline_details(pipeline_id);
287 let was_animating = pipeline_details.animating();
288 pipeline_details.throttled = throttled;
289 let started_animating = !was_animating && pipeline_details.animating();
290
291 self.update_animation_state();
293
294 started_animating
299 }
300
301 fn update_animation_state(&mut self) {
302 self.animating = self.pipelines.values().any(PipelineDetails::animating);
303 self.webview.set_animating(self.animating());
304 }
305
306 pub(crate) fn update_touch_handling_at_new_frame_start(&mut self) -> bool {
307 let Some(fling_action) = self.touch_handler.notify_new_frame_start() else {
308 return self.touch_handler.currently_in_touch_sequence();
309 };
310
311 self.on_scroll_window_event(
312 Scroll::Delta((-fling_action.delta).into()),
313 fling_action.cursor,
314 );
315 self.touch_handler.currently_in_touch_sequence()
316 }
317
318 fn dispatch_input_event_with_hit_testing(
319 &mut self,
320 render_api: &RenderApi,
321 event: InputEventAndId,
322 ) -> bool {
323 let event_point = event
324 .event
325 .point()
326 .map(|point| point.as_device_point(self.device_pixels_per_page_pixel()));
327 let hit_test_result = match event_point {
328 Some(point) => {
329 let hit_test_result = match event.event {
330 InputEvent::Touch(_) => self.touch_handler.get_hit_test_result_cache_value(),
331 _ => None,
332 }
333 .or_else(|| self.hit_test(render_api, point).into_iter().nth(0));
334 if hit_test_result.is_none() {
335 warn!("Empty hit test result for input event, ignoring.");
336 return false;
337 }
338 hit_test_result
339 },
340 None => None,
341 };
342
343 if let Err(error) = self.embedder_to_constellation_sender.send(
344 EmbedderToConstellationMessage::ForwardInputEvent(self.id, event, hit_test_result),
345 ) {
346 warn!("Sending event to constellation failed ({error:?}).");
347 false
348 } else {
349 true
350 }
351 }
352
353 pub(crate) fn notify_input_event(
354 &mut self,
355 render_api: &RenderApi,
356 event_and_id: InputEventAndId,
357 ) {
358 if let InputEvent::Touch(touch_event) = event_and_id.event {
359 self.on_touch_event(render_api, touch_event, event_and_id.id);
360 return;
361 }
362
363 if let InputEvent::Wheel(wheel_event) = event_and_id.event {
364 self.on_wheel_event(render_api, wheel_event, event_and_id);
365 return;
366 }
367
368 self.dispatch_input_event_with_hit_testing(render_api, event_and_id);
369 }
370
371 fn on_wheel_event(
372 &mut self,
373 render_api: &RenderApi,
374 wheel_event: WheelEvent,
375 event_and_id: InputEventAndId,
376 ) {
377 self.dispatch_input_event_with_hit_testing(render_api, event_and_id);
378
379 let scroll_delta =
381 DeviceVector2D::new(-wheel_event.delta.x as f32, -wheel_event.delta.y as f32);
382 self.notify_scroll_event(Scroll::Delta(scroll_delta.into()), wheel_event.point);
383 }
384
385 fn send_touch_event(
386 &mut self,
387 render_api: &RenderApi,
388 event: TouchEvent,
389 id: InputEventId,
390 ) -> bool {
391 let cancelable = event.is_cancelable();
392 let event_type = event.event_type;
393
394 let input_event_and_id = InputEventAndId {
395 event: InputEvent::Touch(event),
396 id,
397 };
398
399 let result = self.dispatch_input_event_with_hit_testing(render_api, input_event_and_id);
400
401 if cancelable && result {
405 self.touch_handler
406 .add_pending_touch_input_event(id, event_type);
407 }
408
409 result
410 }
411
412 pub(crate) fn on_touch_event(
413 &mut self,
414 render_api: &RenderApi,
415 event: TouchEvent,
416 id: InputEventId,
417 ) {
418 let had_touch_sequence = self.touch_handler.currently_in_touch_sequence();
419 match event.event_type {
420 TouchEventType::Down => self.on_touch_down(render_api, event, id),
421 TouchEventType::Move => self.on_touch_move(render_api, event, id),
422 TouchEventType::Up => self.on_touch_up(render_api, event, id),
423 TouchEventType::Cancel => self.on_touch_cancel(render_api, event, id),
424 }
425 if !had_touch_sequence && self.touch_handler.currently_in_touch_sequence() {
426 self.add_touch_move_refresh_obsever();
427 }
428 }
429
430 fn on_touch_down(&mut self, render_api: &RenderApi, event: TouchEvent, id: InputEventId) {
431 let point = event
432 .point
433 .as_device_point(self.device_pixels_per_page_pixel());
434 self.touch_handler.on_touch_down(event.id, point);
435 self.send_touch_event(render_api, event, id);
436 }
437
438 fn on_touch_move(&mut self, render_api: &RenderApi, mut event: TouchEvent, id: InputEventId) {
439 let point = event
440 .point
441 .as_device_point(self.device_pixels_per_page_pixel());
442 let action = self.touch_handler.on_touch_move(event.id, point);
443 if let Some(action) = action {
444 if self
447 .touch_handler
448 .move_allowed(self.touch_handler.current_sequence_id)
449 {
450 event.disable_cancelable();
452 self.pending_scroll_zoom_events.push(action);
453 }
454 if !self
458 .touch_handler
459 .is_handling_touch_move(self.touch_handler.current_sequence_id) &&
460 self.send_touch_event(render_api, event, id) &&
461 event.is_cancelable()
462 {
463 self.touch_handler
464 .set_handling_touch_move(self.touch_handler.current_sequence_id, true);
465 }
466 }
467 }
468
469 fn on_touch_up(&mut self, render_api: &RenderApi, event: TouchEvent, id: InputEventId) {
470 let point = event
471 .point
472 .as_device_point(self.device_pixels_per_page_pixel());
473 self.touch_handler.on_touch_up(event.id, point);
474 self.send_touch_event(render_api, event, id);
475 }
476
477 fn on_touch_cancel(&mut self, render_api: &RenderApi, event: TouchEvent, id: InputEventId) {
478 let point = event
479 .point
480 .as_device_point(self.device_pixels_per_page_pixel());
481 self.touch_handler.on_touch_cancel(event.id, point);
482 self.send_touch_event(render_api, event, id);
483 }
484
485 fn on_touch_event_processed(
486 &mut self,
487 render_api: &RenderApi,
488 pending_touch_input_event: PendingTouchInputEvent,
489 result: InputEventResult,
490 ) {
491 let PendingTouchInputEvent {
492 sequence_id,
493 event_type,
494 } = pending_touch_input_event;
495
496 if result.contains(InputEventResult::DefaultPrevented) {
497 debug!(
498 "Touch event {:?} in sequence {:?} prevented!",
499 event_type, sequence_id
500 );
501 match event_type {
502 TouchEventType::Down => {
503 self.touch_handler.prevent_click(sequence_id);
505 self.touch_handler.prevent_move(sequence_id);
506 self.touch_handler
507 .remove_pending_touch_move_actions(sequence_id);
508 },
509 TouchEventType::Move => {
510 if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) {
512 info.prevent_move = TouchMoveAllowed::Prevented;
513 if let TouchSequenceState::PendingFling { .. } = info.state {
514 info.state = TouchSequenceState::Finished;
515 }
516 self.touch_handler
517 .set_handling_touch_move(self.touch_handler.current_sequence_id, false);
518 self.touch_handler
519 .remove_pending_touch_move_actions(sequence_id);
520 }
521 },
522 TouchEventType::Up => {
523 let Some(info) = &mut self.touch_handler.get_touch_sequence_mut(sequence_id)
527 else {
528 return;
533 };
534 match info.state {
535 TouchSequenceState::PendingClick(_) => {
536 info.state = TouchSequenceState::Finished;
537 self.touch_handler.remove_touch_sequence(sequence_id);
538 },
539 TouchSequenceState::Flinging { .. } => {
540 },
542 TouchSequenceState::Finished => {
543 self.touch_handler.remove_touch_sequence(sequence_id);
544 },
545 TouchSequenceState::Touching |
546 TouchSequenceState::Panning { .. } |
547 TouchSequenceState::Pinching |
548 TouchSequenceState::MultiTouch |
549 TouchSequenceState::PendingFling { .. } => {
550 },
555 }
556 },
557 TouchEventType::Cancel => {
558 self.touch_handler
561 .remove_pending_touch_move_actions(sequence_id);
562 self.touch_handler.try_remove_touch_sequence(sequence_id);
563 },
564 }
565 } else {
566 debug!(
567 "Touch event {:?} in sequence {:?} allowed",
568 event_type, sequence_id
569 );
570 match event_type {
571 TouchEventType::Down => {},
572 TouchEventType::Move => {
573 self.pending_scroll_zoom_events.extend(
574 self.touch_handler
575 .take_pending_touch_move_actions(sequence_id),
576 );
577 self.touch_handler
578 .set_handling_touch_move(self.touch_handler.current_sequence_id, false);
579 if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) {
580 if info.prevent_move == TouchMoveAllowed::Pending {
581 info.prevent_move = TouchMoveAllowed::Allowed;
582 if let TouchSequenceState::PendingFling { velocity, point } = info.state
583 {
584 info.state = TouchSequenceState::Flinging { velocity, point }
585 }
586 }
587 }
588 },
589 TouchEventType::Up => {
590 let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) else {
591 return;
593 };
594 match info.state {
595 TouchSequenceState::PendingClick(point) => {
596 info.state = TouchSequenceState::Finished;
597 if !info.prevent_click {
600 self.simulate_mouse_click(render_api, point);
601 }
602 self.touch_handler.remove_touch_sequence(sequence_id);
603 },
604 TouchSequenceState::Flinging { .. } => {
605 },
607 TouchSequenceState::Finished => {
608 self.touch_handler.remove_touch_sequence(sequence_id);
609 },
610 TouchSequenceState::Panning { .. } |
611 TouchSequenceState::Pinching |
612 TouchSequenceState::PendingFling { .. } => {
613 },
618 TouchSequenceState::MultiTouch | TouchSequenceState::Touching => {
619 },
621 }
622 },
623 TouchEventType::Cancel => {
624 self.touch_handler
625 .remove_pending_touch_move_actions(sequence_id);
626 self.touch_handler.try_remove_touch_sequence(sequence_id);
627 },
628 }
629 }
630 }
631
632 fn add_touch_move_refresh_obsever(&self) {
633 debug_assert!(self.touch_handler.currently_in_touch_sequence());
634 self.refresh_driver
635 .add_observer(Rc::new(FlingRefreshDriverObserver {
636 webview_id: self.id,
637 }));
638 }
639
640 fn simulate_mouse_click(&mut self, render_api: &RenderApi, point: DevicePoint) {
642 let button = MouseButton::Left;
643 self.dispatch_input_event_with_hit_testing(
644 render_api,
645 InputEvent::MouseMove(MouseMoveEvent::new(point.into())).into(),
646 );
647 self.dispatch_input_event_with_hit_testing(
648 render_api,
649 InputEvent::MouseButton(MouseButtonEvent::new(
650 MouseButtonAction::Down,
651 button,
652 point.into(),
653 ))
654 .into(),
655 );
656 self.dispatch_input_event_with_hit_testing(
657 render_api,
658 InputEvent::MouseButton(MouseButtonEvent::new(
659 MouseButtonAction::Up,
660 button,
661 point.into(),
662 ))
663 .into(),
664 );
665 }
666
667 pub(crate) fn notify_scroll_event(&mut self, scroll: Scroll, point: WebViewPoint) {
668 let point = point.as_device_point(self.device_pixels_per_page_pixel());
669 self.on_scroll_window_event(scroll, point);
670 }
671
672 fn on_scroll_window_event(&mut self, scroll: Scroll, cursor: DevicePoint) {
673 self.pending_scroll_zoom_events
674 .push(ScrollZoomEvent::Scroll(ScrollEvent {
675 scroll,
676 point: cursor,
677 event_count: 1,
678 }));
679 }
680
681 pub(crate) fn process_pending_scroll_and_pinch_zoom_events(
688 &mut self,
689 render_api: &RenderApi,
690 ) -> (PinchZoomResult, Option<ScrollResult>) {
691 if self.pending_scroll_zoom_events.is_empty() {
692 return (PinchZoomResult::DidNotPinchZoom, None);
693 }
694
695 let mut combined_scroll_event: Option<ScrollEvent> = None;
698 let mut new_pinch_zoom = self.pinch_zoom;
699 let device_pixels_per_page_pixel = self.device_pixels_per_page_pixel();
700
701 for scroll_event in self.pending_scroll_zoom_events.drain(..) {
702 match scroll_event {
703 ScrollZoomEvent::PinchZoom(factor, center) => {
704 new_pinch_zoom.zoom(factor, center);
705 },
706 ScrollZoomEvent::Scroll(scroll_event_info) => {
707 let combined_event = match combined_scroll_event.as_mut() {
708 None => {
709 combined_scroll_event = Some(scroll_event_info);
710 continue;
711 },
712 Some(combined_event) => combined_event,
713 };
714
715 match (combined_event.scroll, scroll_event_info.scroll) {
716 (Scroll::Delta(old_delta), Scroll::Delta(new_delta)) => {
717 let old_event_count = combined_event.event_count as f32;
722 combined_event.event_count += 1;
723 let new_event_count = combined_event.event_count as f32;
724 let old_delta =
725 old_delta.as_device_vector(device_pixels_per_page_pixel);
726 let new_delta =
727 new_delta.as_device_vector(device_pixels_per_page_pixel);
728 let delta = (old_delta * old_event_count + new_delta) / new_event_count;
729 combined_event.scroll = Scroll::Delta(delta.into());
730 },
731 (Scroll::Start, _) | (Scroll::End, _) => {
732 break;
734 },
735 (_, Scroll::Start) | (_, Scroll::End) => {
736 *combined_event = scroll_event_info;
739 break;
740 },
741 }
742 },
743 }
744 }
745
746 if let Some(combined_scroll_event) = combined_scroll_event.as_mut() {
750 new_pinch_zoom.pan(
751 &mut combined_scroll_event.scroll,
752 self.device_pixels_per_page_pixel(),
753 )
754 }
755
756 let scroll_result = combined_scroll_event.and_then(|combined_event| {
757 self.scroll_node_at_device_point(
758 render_api,
759 combined_event.point.to_f32(),
760 combined_event.scroll,
761 )
762 });
763 if let Some(scroll_result) = scroll_result.clone() {
764 self.send_scroll_positions_to_layout_for_pipeline(
765 scroll_result.hit_test_result.pipeline_id,
766 );
767 self.dispatch_scroll_event(
768 scroll_result.external_scroll_id,
769 scroll_result.hit_test_result,
770 );
771 }
772
773 (self.set_pinch_zoom(new_pinch_zoom), scroll_result)
774 }
775
776 fn scroll_node_at_device_point(
781 &mut self,
782 render_api: &RenderApi,
783 cursor: DevicePoint,
784 scroll: Scroll,
785 ) -> Option<ScrollResult> {
786 let scroll_location = match scroll {
787 Scroll::Delta(delta) => {
788 let device_pixels_per_page = self.device_pixels_per_page_pixel();
789 let calculate_delta =
790 delta.as_device_vector(device_pixels_per_page) / device_pixels_per_page;
791 ScrollLocation::Delta(calculate_delta.cast_unit())
792 },
793 Scroll::Start => ScrollLocation::Start,
794 Scroll::End => ScrollLocation::End,
795 };
796
797 let hit_test_results: Vec<_> = self
798 .touch_handler
799 .get_hit_test_result_cache_value()
800 .map(|result| vec![result])
801 .unwrap_or_else(|| self.hit_test(render_api, cursor));
802
803 let mut previous_pipeline_id = None;
807 for hit_test_result in hit_test_results.iter() {
808 let pipeline_details = self.pipelines.get_mut(&hit_test_result.pipeline_id)?;
809 if previous_pipeline_id.replace(&hit_test_result.pipeline_id) !=
810 Some(&hit_test_result.pipeline_id)
811 {
812 let scroll_result = pipeline_details.scroll_tree.scroll_node_or_ancestor(
813 hit_test_result.external_scroll_id,
814 scroll_location,
815 ScrollType::InputEvents,
816 );
817 if let Some((external_scroll_id, offset)) = scroll_result {
818 self.touch_handler.set_hit_test_result_cache_value(
824 hit_test_result.clone(),
825 self.device_pixels_per_page_pixel(),
826 );
827 return Some(ScrollResult {
828 hit_test_result: hit_test_result.clone(),
829 external_scroll_id,
830 offset,
831 });
832 }
833 }
834 }
835 None
836 }
837
838 pub(crate) fn scroll_viewport_by_delta(
842 &mut self,
843 delta: LayoutVector2D,
844 ) -> (PinchZoomResult, Vec<ScrollResult>) {
845 let device_pixels_per_page_pixel = self.device_pixels_per_page_pixel();
846 let delta_in_device_pixels = delta.cast_unit() * device_pixels_per_page_pixel;
847 let remaining = self.pinch_zoom.pan_with_device_scroll(
848 Scroll::Delta(delta_in_device_pixels.into()),
849 device_pixels_per_page_pixel,
850 );
851
852 let pinch_zoom_result = match remaining == delta_in_device_pixels {
853 true => PinchZoomResult::DidNotPinchZoom,
854 false => PinchZoomResult::DidPinchZoom,
855 };
856 if remaining == Vector2D::zero() {
857 return (pinch_zoom_result, vec![]);
858 }
859
860 let Some(root_pipeline_id) = self.root_pipeline_id else {
861 return (pinch_zoom_result, vec![]);
862 };
863 let Some(root_pipeline) = self.pipelines.get_mut(&root_pipeline_id) else {
864 return (pinch_zoom_result, vec![]);
865 };
866
867 let remaining = remaining / device_pixels_per_page_pixel;
868 let Some((external_scroll_id, offset)) = root_pipeline.scroll_tree.scroll_node_or_ancestor(
869 ExternalScrollId(0, root_pipeline_id.into()),
870 ScrollLocation::Delta(remaining.cast_unit()),
871 ScrollType::InputEvents,
873 ) else {
874 return (pinch_zoom_result, vec![]);
875 };
876
877 let hit_test_result = CompositorHitTestResult {
878 pipeline_id: root_pipeline_id,
879 point_in_viewport: Default::default(),
882 external_scroll_id,
883 };
884
885 self.send_scroll_positions_to_layout_for_pipeline(root_pipeline_id);
886 self.dispatch_scroll_event(external_scroll_id, hit_test_result.clone());
887
888 let scroll_result = ScrollResult {
889 hit_test_result,
890 external_scroll_id,
891 offset,
892 };
893 (pinch_zoom_result, vec![scroll_result])
894 }
895
896 fn dispatch_scroll_event(
897 &self,
898 external_id: ExternalScrollId,
899 hit_test_result: CompositorHitTestResult,
900 ) {
901 let event = InputEvent::Scroll(EmbedderScrollEvent { external_id }).into();
902 let msg = EmbedderToConstellationMessage::ForwardInputEvent(
903 self.id,
904 event,
905 Some(hit_test_result),
906 );
907 if let Err(e) = self.embedder_to_constellation_sender.send(msg) {
908 warn!("Sending scroll event to constellation failed ({:?}).", e);
909 }
910 }
911
912 pub(crate) fn pinch_zoom(&self) -> PinchZoom {
913 self.pinch_zoom
914 }
915
916 fn set_pinch_zoom(&mut self, requested_pinch_zoom: PinchZoom) -> PinchZoomResult {
917 if requested_pinch_zoom == self.pinch_zoom {
918 return PinchZoomResult::DidNotPinchZoom;
919 }
920
921 self.pinch_zoom = requested_pinch_zoom;
922 PinchZoomResult::DidPinchZoom
923 }
924
925 pub(crate) fn set_page_zoom(
926 &mut self,
927 new_page_zoom: Scale<f32, CSSPixel, DeviceIndependentPixel>,
928 ) {
929 let new_page_zoom = new_page_zoom.clamp(MIN_PAGE_ZOOM, MAX_PAGE_ZOOM);
930 let old_zoom = std::mem::replace(&mut self.page_zoom, new_page_zoom);
931 if old_zoom != self.page_zoom {
932 self.send_window_size_message();
933 }
934 }
935
936 pub(crate) fn device_pixels_per_page_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> {
942 let viewport_scale = self
943 .root_pipeline_id
944 .and_then(|pipeline_id| self.pipelines.get(&pipeline_id))
945 .and_then(|pipeline| pipeline.viewport_scale)
946 .unwrap_or_else(|| self.page_zoom * self.hidpi_scale_factor);
947 viewport_scale * self.pinch_zoom.zoom_factor()
948 }
949
950 pub(crate) fn device_pixels_per_page_pixel_not_including_pinch_zoom(
955 &self,
956 ) -> Scale<f32, CSSPixel, DevicePixel> {
957 self.page_zoom * self.hidpi_scale_factor
958 }
959
960 pub(crate) fn adjust_pinch_zoom(&mut self, magnification: f32, center: DevicePoint) {
962 if magnification == 1.0 {
963 return;
964 }
965
966 self.pending_scroll_zoom_events
967 .push(ScrollZoomEvent::PinchZoom(magnification, center));
968 }
969
970 fn send_window_size_message(&self) {
971 let device_pixel_ratio = self.device_pixels_per_page_pixel_not_including_pinch_zoom();
974 let initial_viewport = self.rect.size().to_f32() / device_pixel_ratio;
975 let _ = self.embedder_to_constellation_sender.send(
976 EmbedderToConstellationMessage::ChangeViewportDetails(
977 self.id,
978 ViewportDetails {
979 hidpi_scale_factor: device_pixel_ratio,
980 size: initial_viewport,
981 },
982 WindowSizeType::Resize,
983 ),
984 );
985 }
986
987 pub(crate) fn set_hidpi_scale_factor(
989 &mut self,
990 new_scale: Scale<f32, DeviceIndependentPixel, DevicePixel>,
991 ) -> bool {
992 let old_scale_factor = std::mem::replace(&mut self.hidpi_scale_factor, new_scale);
993 if self.hidpi_scale_factor == old_scale_factor {
994 return false;
995 }
996
997 self.send_window_size_message();
998 true
999 }
1000
1001 pub(crate) fn set_rect(&mut self, new_rect: DeviceRect) -> bool {
1003 let old_rect = std::mem::replace(&mut self.rect, new_rect);
1004 if old_rect.size() != self.rect.size() {
1005 self.send_window_size_message();
1006 }
1007 old_rect != self.rect
1008 }
1009
1010 pub fn set_viewport_description(&mut self, viewport_description: ViewportDescription) {
1011 self.set_page_zoom(Scale::new(
1012 viewport_description.clamp_page_zoom(viewport_description.initial_scale.get()),
1013 ));
1014 self.viewport_description = Some(viewport_description);
1015 }
1016
1017 pub(crate) fn scroll_trees_memory_usage(
1018 &self,
1019 ops: &mut malloc_size_of::MallocSizeOfOps,
1020 ) -> usize {
1021 self.pipelines
1022 .values()
1023 .map(|pipeline| pipeline.scroll_tree.size_of(ops))
1024 .sum::<usize>()
1025 }
1026
1027 pub(crate) fn notify_input_event_handled(
1028 &mut self,
1029 render_api: &RenderApi,
1030 id: InputEventId,
1031 result: InputEventResult,
1032 ) {
1033 if let Some(pending_touch_input_event) =
1034 self.touch_handler.take_pending_touch_input_event(id)
1035 {
1036 self.on_touch_event_processed(render_api, pending_touch_input_event, result);
1037 }
1038 }
1039}
1040
1041#[derive(Clone, Copy, Debug, PartialEq)]
1042pub struct UnknownWebView(pub WebViewId);