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