1use std::cell::RefCell;
6use std::collections::hash_map::{Entry, Keys};
7use std::rc::Rc;
8
9use base::id::{PipelineId, WebViewId};
10use compositing_traits::display_list::ScrollType;
11use compositing_traits::viewport_description::{
12 DEFAULT_PAGE_ZOOM, MAX_PAGE_ZOOM, MIN_PAGE_ZOOM, ViewportDescription,
13};
14use compositing_traits::{PipelineExitSource, SendableFrameTree, WebViewTrait};
15use constellation_traits::{EmbedderToConstellationMessage, WindowSizeType};
16use embedder_traits::{
17 AnimationState, CompositorHitTestResult, InputEvent, MouseButton, MouseButtonAction,
18 MouseButtonEvent, MouseMoveEvent, ScrollEvent as EmbedderScrollEvent, ShutdownState,
19 TouchEvent, TouchEventResult, TouchEventType, TouchId, ViewportDetails,
20};
21use euclid::{Point2D, Scale, Vector2D};
22use log::{debug, warn};
23use malloc_size_of::MallocSizeOf;
24use rustc_hash::{FxHashMap, FxHashSet};
25use servo_geometry::DeviceIndependentPixel;
26use style_traits::{CSSPixel, PinchZoomFactor};
27use webrender_api::units::{DeviceIntPoint, DevicePixel, DevicePoint, DeviceRect, LayoutVector2D};
28use webrender_api::{ExternalScrollId, HitTestFlags, ScrollLocation};
29
30use crate::compositor::{PipelineDetails, ServoRenderer};
31use crate::touch::{TouchHandler, TouchMoveAction, TouchMoveAllowed, TouchSequenceState};
32
33#[derive(Clone, Copy)]
34struct ScrollEvent {
35 scroll_location: ScrollLocation,
37 cursor: DeviceIntPoint,
39 event_count: u32,
41}
42
43#[derive(Clone, Copy)]
44enum ScrollZoomEvent {
45 PinchZoom(f32),
47 InitialViewportZoom(f32),
49 Scroll(ScrollEvent),
52}
53
54#[derive(Clone, Debug)]
55pub(crate) struct ScrollResult {
56 pub hit_test_result: CompositorHitTestResult,
57 pub external_scroll_id: ExternalScrollId,
58 pub offset: LayoutVector2D,
59}
60
61#[derive(Debug, PartialEq)]
62pub(crate) enum PinchZoomResult {
63 DidPinchZoom,
64 DidNotPinchZoom,
65}
66
67pub(crate) struct WebViewRenderer {
71 pub id: WebViewId,
73 pub webview: Box<dyn WebViewTrait>,
77 pub root_pipeline_id: Option<PipelineId>,
79 pub rect: DeviceRect,
81 pub pipelines: FxHashMap<PipelineId, PipelineDetails>,
83 pub(crate) global: Rc<RefCell<ServoRenderer>>,
85 pending_scroll_zoom_events: Vec<ScrollZoomEvent>,
87 touch_handler: TouchHandler,
89 pub page_zoom: Scale<f32, CSSPixel, DeviceIndependentPixel>,
91 pinch_zoom: PinchZoomFactor,
93 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
96 animating: bool,
99 viewport_description: Option<ViewportDescription>,
102}
103
104impl WebViewRenderer {
105 pub(crate) fn new(
106 global: Rc<RefCell<ServoRenderer>>,
107 renderer_webview: Box<dyn WebViewTrait>,
108 viewport_details: ViewportDetails,
109 ) -> Self {
110 let hidpi_scale_factor = viewport_details.hidpi_scale_factor;
111 let size = viewport_details.size * viewport_details.hidpi_scale_factor;
112 Self {
113 id: renderer_webview.id(),
114 webview: renderer_webview,
115 root_pipeline_id: None,
116 rect: DeviceRect::from_origin_and_size(DevicePoint::origin(), size),
117 pipelines: Default::default(),
118 touch_handler: TouchHandler::new(),
119 global,
120 pending_scroll_zoom_events: Default::default(),
121 page_zoom: DEFAULT_PAGE_ZOOM,
122 pinch_zoom: PinchZoomFactor::new(1.0),
123 hidpi_scale_factor: Scale::new(hidpi_scale_factor.0),
124 animating: false,
125 viewport_description: None,
126 }
127 }
128
129 pub(crate) fn animation_callbacks_running(&self) -> bool {
130 self.pipelines
131 .values()
132 .any(PipelineDetails::animation_callbacks_running)
133 }
134
135 pub(crate) fn pipeline_ids(&self) -> Keys<'_, PipelineId, PipelineDetails> {
136 self.pipelines.keys()
137 }
138
139 pub(crate) fn animating(&self) -> bool {
140 self.animating
141 }
142
143 pub(crate) fn ensure_pipeline_details(
145 &mut self,
146 pipeline_id: PipelineId,
147 ) -> &mut PipelineDetails {
148 self.pipelines
149 .entry(pipeline_id)
150 .or_insert_with(PipelineDetails::new)
151 }
152
153 pub(crate) fn pipeline_exited(&mut self, pipeline_id: PipelineId, source: PipelineExitSource) {
154 let pipeline = self.pipelines.entry(pipeline_id);
155 let Entry::Occupied(mut pipeline) = pipeline else {
156 return;
157 };
158
159 pipeline.get_mut().exited.insert(source);
160
161 if !pipeline.get().exited.is_all() {
165 return;
166 }
167
168 pipeline.remove_entry();
169 }
170
171 pub(crate) fn set_frame_tree(&mut self, frame_tree: &SendableFrameTree) {
172 let pipeline_id = frame_tree.pipeline.id;
173 let old_pipeline_id = self.root_pipeline_id.replace(pipeline_id);
174
175 if old_pipeline_id != self.root_pipeline_id {
176 debug!(
177 "Updating webview ({:?}) from pipeline {:?} to {:?}",
178 3, old_pipeline_id, self.root_pipeline_id
179 );
180 }
181
182 self.set_frame_tree_on_pipeline_details(frame_tree, None);
183 self.reset_scroll_tree_for_unattached_pipelines(frame_tree);
184 self.send_scroll_positions_to_layout_for_pipeline(pipeline_id);
185 }
186
187 pub(crate) fn send_scroll_positions_to_layout_for_pipeline(&self, pipeline_id: PipelineId) {
188 let Some(details) = self.pipelines.get(&pipeline_id) else {
189 return;
190 };
191
192 let scroll_offsets = details.scroll_tree.scroll_offsets();
193
194 if scroll_offsets.is_empty() {
199 return;
200 }
201
202 let _ = self.global.borrow().constellation_sender.send(
203 EmbedderToConstellationMessage::SetScrollStates(pipeline_id, scroll_offsets),
204 );
205 }
206
207 pub(crate) fn set_frame_tree_on_pipeline_details(
208 &mut self,
209 frame_tree: &SendableFrameTree,
210 parent_pipeline_id: Option<PipelineId>,
211 ) {
212 let pipeline_id = frame_tree.pipeline.id;
213 let pipeline_details = self.ensure_pipeline_details(pipeline_id);
214 pipeline_details.pipeline = Some(frame_tree.pipeline.clone());
215 pipeline_details.parent_pipeline_id = parent_pipeline_id;
216
217 for kid in &frame_tree.children {
218 self.set_frame_tree_on_pipeline_details(kid, Some(pipeline_id));
219 }
220 }
221
222 pub(crate) fn reset_scroll_tree_for_unattached_pipelines(
223 &mut self,
224 frame_tree: &SendableFrameTree,
225 ) {
226 fn collect_pipelines(
230 pipelines: &mut FxHashSet<PipelineId>,
231 frame_tree: &SendableFrameTree,
232 ) {
233 pipelines.insert(frame_tree.pipeline.id);
234 for kid in &frame_tree.children {
235 collect_pipelines(pipelines, kid);
236 }
237 }
238
239 let mut attached_pipelines: FxHashSet<PipelineId> = FxHashSet::default();
240 collect_pipelines(&mut attached_pipelines, frame_tree);
241
242 self.pipelines
243 .iter_mut()
244 .filter(|(id, _)| !attached_pipelines.contains(id))
245 .for_each(|(_, details)| details.scroll_tree.reset_all_scroll_offsets());
246 }
247
248 pub(crate) fn change_pipeline_running_animations_state(
251 &mut self,
252 pipeline_id: PipelineId,
253 animation_state: AnimationState,
254 ) -> bool {
255 let pipeline_details = self.ensure_pipeline_details(pipeline_id);
256 let was_animating = pipeline_details.animating();
257 match animation_state {
258 AnimationState::AnimationsPresent => {
259 pipeline_details.animations_running = true;
260 },
261 AnimationState::AnimationCallbacksPresent => {
262 pipeline_details.animation_callbacks_running = true;
263 },
264 AnimationState::NoAnimationsPresent => {
265 pipeline_details.animations_running = false;
266 },
267 AnimationState::NoAnimationCallbacksPresent => {
268 pipeline_details.animation_callbacks_running = false;
269 },
270 }
271 let started_animating = !was_animating && pipeline_details.animating();
272
273 self.update_animation_state();
274
275 started_animating
280 }
281
282 pub(crate) fn set_throttled(&mut self, pipeline_id: PipelineId, throttled: bool) -> bool {
285 let pipeline_details = self.ensure_pipeline_details(pipeline_id);
286 let was_animating = pipeline_details.animating();
287 pipeline_details.throttled = throttled;
288 let started_animating = !was_animating && pipeline_details.animating();
289
290 self.update_animation_state();
292
293 started_animating
298 }
299
300 fn update_animation_state(&mut self) {
301 self.animating = self.pipelines.values().any(PipelineDetails::animating);
302 self.webview.set_animating(self.animating());
303 }
304
305 pub(crate) fn on_vsync(&mut self) {
307 if let Some(fling_action) = self.touch_handler.on_vsync() {
308 self.on_scroll_window_event(
309 ScrollLocation::Delta(-fling_action.delta),
310 fling_action.cursor,
311 );
312 }
313 }
314
315 pub(crate) fn dispatch_input_event_with_hit_testing(&self, mut event: InputEvent) -> bool {
316 let event_point = event.point();
317 let hit_test_result = match event_point {
318 Some(point) => {
319 let hit_test_result = self
320 .global
321 .borrow()
322 .hit_test_at_point(point)
323 .into_iter()
324 .nth(0);
325 if hit_test_result.is_none() {
326 warn!("Empty hit test result for input event, ignoring.");
327 return false;
328 }
329 hit_test_result
330 },
331 None => None,
332 };
333
334 match event {
335 InputEvent::Touch(ref mut touch_event) => {
336 touch_event.init_sequence_id(self.touch_handler.current_sequence_id);
337 },
338 InputEvent::MouseMove(_) => {
339 self.global.borrow_mut().last_mouse_move_position = event_point;
340 },
341 InputEvent::MouseLeftViewport(_) => {
342 self.global.borrow_mut().last_mouse_move_position = None;
343 },
344 InputEvent::MouseButton(_) | InputEvent::Wheel(_) => {},
345 _ => unreachable!("Unexpected input event type: {event:?}"),
346 }
347
348 if let Err(error) = self.global.borrow().constellation_sender.send(
349 EmbedderToConstellationMessage::ForwardInputEvent(self.id, event, hit_test_result),
350 ) {
351 warn!("Sending event to constellation failed ({error:?}).");
352 false
353 } else {
354 true
355 }
356 }
357
358 pub(crate) fn notify_input_event(&mut self, event: InputEvent) {
359 if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
360 return;
361 }
362
363 if let InputEvent::Touch(event) = event {
364 self.on_touch_event(event);
365 return;
366 }
367
368 if self.global.borrow().convert_mouse_to_touch {
369 match event {
370 InputEvent::MouseButton(event) => {
371 match (event.button, event.action) {
372 (MouseButton::Left, MouseButtonAction::Down) => self.on_touch_down(
373 TouchEvent::new(TouchEventType::Down, TouchId(0), event.point),
374 ),
375 (MouseButton::Left, MouseButtonAction::Up) => self.on_touch_up(
376 TouchEvent::new(TouchEventType::Up, TouchId(0), event.point),
377 ),
378 _ => {},
379 }
380 return;
381 },
382 InputEvent::MouseMove(event) => {
383 if let Some(state) = self.touch_handler.try_get_current_touch_sequence() {
384 match state.state {
388 TouchSequenceState::Touching | TouchSequenceState::Panning { .. } => {
389 self.on_touch_move(TouchEvent::new(
390 TouchEventType::Move,
391 TouchId(0),
392 event.point,
393 ));
394 },
395 TouchSequenceState::MultiTouch => {
396 },
400 TouchSequenceState::Pinching => {
401 #[cfg(debug_assertions)]
403 log::error!(
404 "Touch handler is in Pinching state, which should be unreachable with \
405 -Z convert-mouse-to-touch debug option."
406 );
407 },
408 TouchSequenceState::PendingFling { .. } |
409 TouchSequenceState::Flinging { .. } |
410 TouchSequenceState::PendingClick(_) |
411 TouchSequenceState::Finished => {
412 },
415 }
416 }
417 return;
419 },
420 _ => {},
421 }
422 }
423
424 self.dispatch_input_event_with_hit_testing(event);
425 }
426
427 fn send_touch_event(&mut self, event: TouchEvent) -> bool {
428 self.dispatch_input_event_with_hit_testing(InputEvent::Touch(event))
429 }
430
431 pub(crate) fn on_touch_event(&mut self, event: TouchEvent) {
432 if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
433 return;
434 }
435
436 match event.event_type {
437 TouchEventType::Down => self.on_touch_down(event),
438 TouchEventType::Move => self.on_touch_move(event),
439 TouchEventType::Up => self.on_touch_up(event),
440 TouchEventType::Cancel => self.on_touch_cancel(event),
441 }
442 }
443
444 fn on_touch_down(&mut self, event: TouchEvent) {
445 self.touch_handler.on_touch_down(event.id, event.point);
446 self.send_touch_event(event);
447 }
448
449 fn on_touch_move(&mut self, mut event: TouchEvent) {
450 let action: TouchMoveAction = self.touch_handler.on_touch_move(event.id, event.point);
451 if TouchMoveAction::NoAction != action {
452 if self
455 .touch_handler
456 .move_allowed(self.touch_handler.current_sequence_id)
457 {
458 event.disable_cancelable();
460 match action {
461 TouchMoveAction::Scroll(delta, point) => self.on_scroll_window_event(
462 ScrollLocation::Delta(LayoutVector2D::from_untyped(delta.to_untyped())),
463 point.cast(),
464 ),
465 TouchMoveAction::Zoom(magnification, scroll_delta) => {
466 let cursor = Point2D::new(-1, -1); self.pending_scroll_zoom_events
472 .push(ScrollZoomEvent::PinchZoom(magnification));
473 self.pending_scroll_zoom_events
474 .push(ScrollZoomEvent::Scroll(ScrollEvent {
475 scroll_location: ScrollLocation::Delta(
476 LayoutVector2D::from_untyped(scroll_delta.to_untyped()),
477 ),
478 cursor,
479 event_count: 1,
480 }));
481 },
482 _ => {},
483 }
484 }
485 if !self
489 .touch_handler
490 .is_handling_touch_move(self.touch_handler.current_sequence_id) &&
491 self.send_touch_event(event) &&
492 event.is_cancelable()
493 {
494 self.touch_handler
495 .set_handling_touch_move(self.touch_handler.current_sequence_id, true);
496 }
497 }
498 }
499
500 fn on_touch_up(&mut self, event: TouchEvent) {
501 self.touch_handler.on_touch_up(event.id, event.point);
502 self.send_touch_event(event);
503 }
504
505 fn on_touch_cancel(&mut self, event: TouchEvent) {
506 self.touch_handler.on_touch_cancel(event.id, event.point);
508 self.send_touch_event(event);
509 }
510
511 pub(crate) fn on_touch_event_processed(&mut self, result: TouchEventResult) {
512 match result {
513 TouchEventResult::DefaultPrevented(sequence_id, event_type) => {
514 debug!(
515 "Touch event {:?} in sequence {:?} prevented!",
516 event_type, sequence_id
517 );
518 match event_type {
519 TouchEventType::Down => {
520 self.touch_handler.prevent_click(sequence_id);
522 self.touch_handler.prevent_move(sequence_id);
523 self.touch_handler
524 .remove_pending_touch_move_action(sequence_id);
525 },
526 TouchEventType::Move => {
527 if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) {
529 info.prevent_move = TouchMoveAllowed::Prevented;
530 if let TouchSequenceState::PendingFling { .. } = info.state {
531 info.state = TouchSequenceState::Finished;
532 }
533 self.touch_handler.set_handling_touch_move(
534 self.touch_handler.current_sequence_id,
535 false,
536 );
537 self.touch_handler
538 .remove_pending_touch_move_action(sequence_id);
539 }
540 },
541 TouchEventType::Up => {
542 let Some(info) =
546 &mut self.touch_handler.get_touch_sequence_mut(sequence_id)
547 else {
548 return;
553 };
554 match info.state {
555 TouchSequenceState::PendingClick(_) => {
556 info.state = TouchSequenceState::Finished;
557 self.touch_handler.remove_touch_sequence(sequence_id);
558 },
559 TouchSequenceState::Flinging { .. } => {
560 },
562 TouchSequenceState::Finished => {
563 self.touch_handler.remove_touch_sequence(sequence_id);
564 },
565 TouchSequenceState::Touching |
566 TouchSequenceState::Panning { .. } |
567 TouchSequenceState::Pinching |
568 TouchSequenceState::MultiTouch |
569 TouchSequenceState::PendingFling { .. } => {
570 },
575 }
576 },
577 TouchEventType::Cancel => {
578 self.touch_handler
581 .remove_pending_touch_move_action(sequence_id);
582 self.touch_handler.try_remove_touch_sequence(sequence_id);
583 },
584 }
585 },
586 TouchEventResult::DefaultAllowed(sequence_id, event_type) => {
587 debug!(
588 "Touch event {:?} in sequence {:?} allowed",
589 event_type, sequence_id
590 );
591 match event_type {
592 TouchEventType::Down => {},
593 TouchEventType::Move => {
594 if let Some(action) =
595 self.touch_handler.pending_touch_move_action(sequence_id)
596 {
597 match action {
598 TouchMoveAction::Scroll(delta, point) => self
599 .on_scroll_window_event(
600 ScrollLocation::Delta(LayoutVector2D::from_untyped(
601 delta.to_untyped(),
602 )),
603 point.cast(),
604 ),
605 TouchMoveAction::Zoom(magnification, scroll_delta) => {
606 let cursor = Point2D::new(-1, -1);
607 self.pending_scroll_zoom_events
612 .push(ScrollZoomEvent::PinchZoom(magnification));
613 self.pending_scroll_zoom_events
614 .push(ScrollZoomEvent::Scroll(ScrollEvent {
615 scroll_location: ScrollLocation::Delta(
616 LayoutVector2D::from_untyped(
617 scroll_delta.to_untyped(),
618 ),
619 ),
620 cursor,
621 event_count: 1,
622 }));
623 },
624 TouchMoveAction::NoAction => {
625 },
627 }
628 self.touch_handler
629 .remove_pending_touch_move_action(sequence_id);
630 }
631 self.touch_handler
632 .set_handling_touch_move(self.touch_handler.current_sequence_id, false);
633 if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) {
634 if info.prevent_move == TouchMoveAllowed::Pending {
635 info.prevent_move = TouchMoveAllowed::Allowed;
636 if let TouchSequenceState::PendingFling { velocity, cursor } =
637 info.state
638 {
639 info.state = TouchSequenceState::Flinging { velocity, cursor }
640 }
641 }
642 }
643 },
644 TouchEventType::Up => {
645 let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id)
646 else {
647 return;
649 };
650 match info.state {
651 TouchSequenceState::PendingClick(point) => {
652 info.state = TouchSequenceState::Finished;
653 if !info.prevent_click {
656 self.simulate_mouse_click(point);
657 }
658 self.touch_handler.remove_touch_sequence(sequence_id);
659 },
660 TouchSequenceState::Flinging { .. } => {
661 },
663 TouchSequenceState::Finished => {
664 self.touch_handler.remove_touch_sequence(sequence_id);
665 },
666 TouchSequenceState::Panning { .. } |
667 TouchSequenceState::Pinching |
668 TouchSequenceState::PendingFling { .. } => {
669 },
674 TouchSequenceState::MultiTouch | TouchSequenceState::Touching => {
675 },
677 }
678 },
679 TouchEventType::Cancel => {
680 self.touch_handler
681 .remove_pending_touch_move_action(sequence_id);
682 self.touch_handler.try_remove_touch_sequence(sequence_id);
683 },
684 }
685 },
686 }
687 }
688
689 fn simulate_mouse_click(&mut self, point: DevicePoint) {
691 let button = MouseButton::Left;
692 self.dispatch_input_event_with_hit_testing(InputEvent::MouseMove(MouseMoveEvent::new(
693 point,
694 )));
695 self.dispatch_input_event_with_hit_testing(InputEvent::MouseButton(MouseButtonEvent::new(
696 MouseButtonAction::Down,
697 button,
698 point,
699 )));
700 self.dispatch_input_event_with_hit_testing(InputEvent::MouseButton(MouseButtonEvent::new(
701 MouseButtonAction::Up,
702 button,
703 point,
704 )));
705 }
706
707 pub(crate) fn notify_scroll_event(
708 &mut self,
709 scroll_location: ScrollLocation,
710 cursor: DeviceIntPoint,
711 ) {
712 if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
713 return;
714 }
715 self.on_scroll_window_event(scroll_location, cursor);
716 }
717
718 fn on_scroll_window_event(&mut self, scroll_location: ScrollLocation, cursor: DeviceIntPoint) {
719 self.pending_scroll_zoom_events
720 .push(ScrollZoomEvent::Scroll(ScrollEvent {
721 scroll_location,
722 cursor,
723 event_count: 1,
724 }));
725 }
726
727 pub(crate) fn process_pending_scroll_and_pinch_zoom_events(
734 &mut self,
735 ) -> (PinchZoomResult, Option<ScrollResult>) {
736 if self.pending_scroll_zoom_events.is_empty() {
737 return (PinchZoomResult::DidNotPinchZoom, None);
738 }
739
740 let mut combined_scroll_event: Option<ScrollEvent> = None;
742 let mut base_page_zoom = self.pinch_zoom_level().get();
743 let mut combined_magnification = 1.0;
744 for scroll_event in self.pending_scroll_zoom_events.drain(..) {
745 match scroll_event {
746 ScrollZoomEvent::PinchZoom(magnification) => {
747 combined_magnification *= magnification
748 },
749 ScrollZoomEvent::InitialViewportZoom(magnification) => {
750 base_page_zoom = magnification
751 },
752 ScrollZoomEvent::Scroll(scroll_event_info) => {
753 let combined_event = match combined_scroll_event.as_mut() {
754 None => {
755 combined_scroll_event = Some(scroll_event_info);
756 continue;
757 },
758 Some(combined_event) => combined_event,
759 };
760
761 match (
762 combined_event.scroll_location,
763 scroll_event_info.scroll_location,
764 ) {
765 (ScrollLocation::Delta(old_delta), ScrollLocation::Delta(new_delta)) => {
766 let old_event_count = Scale::new(combined_event.event_count as f32);
771 combined_event.event_count += 1;
772 let new_event_count = Scale::new(combined_event.event_count as f32);
773 combined_event.scroll_location = ScrollLocation::Delta(
774 (old_delta * old_event_count + new_delta) / new_event_count,
775 );
776 },
777 (ScrollLocation::Start, _) | (ScrollLocation::End, _) => {
778 break;
780 },
781 (_, ScrollLocation::Start) | (_, ScrollLocation::End) => {
782 *combined_event = scroll_event_info;
785 break;
786 },
787 }
788 },
789 }
790 }
791
792 let scroll_result = combined_scroll_event.and_then(|combined_event| {
793 self.scroll_node_at_device_point(
794 combined_event.cursor.to_f32(),
795 combined_event.scroll_location,
796 )
797 });
798 if let Some(scroll_result) = scroll_result.clone() {
799 self.send_scroll_positions_to_layout_for_pipeline(
800 scroll_result.hit_test_result.pipeline_id,
801 );
802 self.dispatch_scroll_event(
803 scroll_result.external_scroll_id,
804 scroll_result.hit_test_result,
805 );
806 }
807
808 let pinch_zoom_result =
809 match self.set_pinch_zoom_level(base_page_zoom * combined_magnification) {
810 true => PinchZoomResult::DidPinchZoom,
811 false => PinchZoomResult::DidNotPinchZoom,
812 };
813
814 (pinch_zoom_result, scroll_result)
815 }
816
817 fn scroll_node_at_device_point(
822 &mut self,
823 cursor: DevicePoint,
824 scroll_location: ScrollLocation,
825 ) -> Option<ScrollResult> {
826 let scroll_location = match scroll_location {
827 ScrollLocation::Delta(delta) => {
828 let device_pixels_per_page = self.device_pixels_per_page_pixel();
829 let scaled_delta = (Vector2D::from_untyped(delta.to_untyped()) /
830 device_pixels_per_page)
831 .to_untyped();
832 let calculated_delta = LayoutVector2D::from_untyped(scaled_delta);
833 ScrollLocation::Delta(calculated_delta)
834 },
835 ScrollLocation::Start | ScrollLocation::End => scroll_location,
837 };
838
839 let hit_test_results = self
840 .global
841 .borrow()
842 .hit_test_at_point_with_flags(cursor, HitTestFlags::FIND_ALL);
843
844 let mut previous_pipeline_id = None;
848 for hit_test_result in hit_test_results.iter() {
849 let pipeline_details = self.pipelines.get_mut(&hit_test_result.pipeline_id)?;
850 if previous_pipeline_id.replace(&hit_test_result.pipeline_id) !=
851 Some(&hit_test_result.pipeline_id)
852 {
853 let scroll_result = pipeline_details.scroll_tree.scroll_node_or_ancestor(
854 hit_test_result.external_scroll_id,
855 scroll_location,
856 ScrollType::InputEvents,
857 );
858 if let Some((external_scroll_id, offset)) = scroll_result {
859 return Some(ScrollResult {
860 hit_test_result: hit_test_result.clone(),
861 external_scroll_id,
862 offset,
863 });
864 }
865 }
866 }
867 None
868 }
869
870 fn dispatch_scroll_event(
871 &self,
872 external_id: ExternalScrollId,
873 hit_test_result: CompositorHitTestResult,
874 ) {
875 let event = InputEvent::Scroll(EmbedderScrollEvent { external_id });
876 let msg = EmbedderToConstellationMessage::ForwardInputEvent(
877 self.id,
878 event,
879 Some(hit_test_result),
880 );
881 if let Err(e) = self.global.borrow().constellation_sender.send(msg) {
882 warn!("Sending scroll event to constellation failed ({:?}).", e);
883 }
884 }
885
886 pub(crate) fn pinch_zoom_level(&self) -> Scale<f32, DevicePixel, DevicePixel> {
887 Scale::new(self.pinch_zoom.get())
888 }
889
890 fn set_pinch_zoom_level(&mut self, mut zoom: f32) -> bool {
891 if let Some(viewport) = self.viewport_description.as_ref() {
892 zoom = viewport.clamp_zoom(zoom);
893 }
894
895 let old_zoom = std::mem::replace(&mut self.pinch_zoom, PinchZoomFactor::new(zoom));
896 old_zoom != self.pinch_zoom
897 }
898
899 pub(crate) fn page_zoom(&mut self) -> Scale<f32, CSSPixel, DeviceIndependentPixel> {
900 self.page_zoom
901 }
902
903 pub(crate) fn set_page_zoom(
904 &mut self,
905 new_page_zoom: Scale<f32, CSSPixel, DeviceIndependentPixel>,
906 ) {
907 let new_page_zoom = new_page_zoom.clamp(MIN_PAGE_ZOOM, MAX_PAGE_ZOOM);
908 let old_zoom = std::mem::replace(&mut self.page_zoom, new_page_zoom);
909 if old_zoom != self.page_zoom {
910 self.send_window_size_message();
911 }
912 }
913
914 pub(crate) fn device_pixels_per_page_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> {
920 let viewport_scale = self
921 .root_pipeline_id
922 .and_then(|pipeline_id| self.pipelines.get(&pipeline_id))
923 .and_then(|pipeline| pipeline.viewport_scale)
924 .unwrap_or_else(|| self.page_zoom * self.hidpi_scale_factor);
925 viewport_scale * self.pinch_zoom_level()
926 }
927
928 pub(crate) fn device_pixels_per_page_pixel_not_including_pinch_zoom(
933 &self,
934 ) -> Scale<f32, CSSPixel, DevicePixel> {
935 self.page_zoom * self.hidpi_scale_factor
936 }
937
938 pub(crate) fn set_pinch_zoom(&mut self, magnification: f32) {
940 if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
941 return;
942 }
943
944 self.pending_scroll_zoom_events
946 .push(ScrollZoomEvent::PinchZoom(
947 self.viewport_description
948 .clone()
949 .unwrap_or_default()
950 .clamp_zoom(magnification),
951 ));
952 }
953
954 fn send_window_size_message(&self) {
955 let device_pixel_ratio = self.device_pixels_per_page_pixel_not_including_pinch_zoom();
958 let initial_viewport = self.rect.size().to_f32() / device_pixel_ratio;
959 let msg = EmbedderToConstellationMessage::ChangeViewportDetails(
960 self.id,
961 ViewportDetails {
962 hidpi_scale_factor: device_pixel_ratio,
963 size: initial_viewport,
964 },
965 WindowSizeType::Resize,
966 );
967 if let Err(e) = self.global.borrow().constellation_sender.send(msg) {
968 warn!("Sending window resize to constellation failed ({:?}).", e);
969 }
970 }
971
972 pub(crate) fn set_hidpi_scale_factor(
974 &mut self,
975 new_scale: Scale<f32, DeviceIndependentPixel, DevicePixel>,
976 ) -> bool {
977 let old_scale_factor = std::mem::replace(&mut self.hidpi_scale_factor, new_scale);
978 if self.hidpi_scale_factor == old_scale_factor {
979 return false;
980 }
981
982 self.send_window_size_message();
983 true
984 }
985
986 pub(crate) fn set_rect(&mut self, new_rect: DeviceRect) -> bool {
988 let old_rect = std::mem::replace(&mut self.rect, new_rect);
989 if old_rect.size() != self.rect.size() {
990 self.send_window_size_message();
991 }
992 old_rect != self.rect
993 }
994
995 pub fn set_viewport_description(&mut self, viewport_description: ViewportDescription) {
996 self.pending_scroll_zoom_events
997 .push(ScrollZoomEvent::InitialViewportZoom(
998 viewport_description
999 .clone()
1000 .clamp_zoom(viewport_description.initial_scale.get()),
1001 ));
1002 self.viewport_description = Some(viewport_description);
1003 }
1004
1005 pub(crate) fn scroll_trees_memory_usage(
1006 &self,
1007 ops: &mut malloc_size_of::MallocSizeOfOps,
1008 ) -> usize {
1009 self.pipelines
1010 .values()
1011 .map(|pipeline| pipeline.scroll_tree.size_of(ops))
1012 .sum::<usize>()
1013 }
1014}
1015
1016#[derive(Clone, Copy, Debug, PartialEq)]
1017pub struct UnknownWebView(pub WebViewId);