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