1use std::cell::Cell;
6use std::collections::hash_map::Entry;
7use std::rc::Rc;
8
9use crossbeam_channel::Sender;
10use embedder_traits::{
11 AnimationState, InputEvent, InputEventAndId, InputEventId, InputEventResult, MouseButton,
12 MouseButtonAction, MouseButtonEvent, MouseMoveEvent, PaintHitTestResult, Scroll, TouchEvent,
13 TouchEventType, ViewportDetails, WebViewPoint, WheelEvent,
14};
15use euclid::{Scale, Vector2D};
16use log::{debug, warn};
17use malloc_size_of::MallocSizeOf;
18use paint_api::display_list::ScrollType;
19use paint_api::viewport_description::{
20 DEFAULT_PAGE_ZOOM, MAX_PAGE_ZOOM, MIN_PAGE_ZOOM, ViewportDescription,
21};
22use paint_api::{PipelineExitSource, SendableFrameTree, WebViewTrait};
23use rustc_hash::FxHashMap;
24use servo_base::id::{PipelineId, WebViewId};
25use servo_constellation_traits::{
26 EmbedderToConstellationMessage, ScrollStateUpdate, WindowSizeType,
27};
28use servo_geometry::DeviceIndependentPixel;
29use style_traits::CSSPixel;
30use webrender::RenderApi;
31use webrender_api::units::{DevicePixel, DevicePoint, DeviceRect, DeviceVector2D, LayoutVector2D};
32use webrender_api::{DocumentId, ExternalScrollId, ScrollLocation};
33
34use crate::paint::RepaintReason;
35use crate::painter::Painter;
36use crate::pinch_zoom::PinchZoom;
37use crate::pipeline_details::PipelineDetails;
38use crate::refresh_driver::BaseRefreshDriver;
39use crate::touch::{
40 PendingTouchInputEvent, TouchHandler, TouchIdMoveTracking, TouchMoveAllowed, TouchSequenceState,
41};
42
43#[derive(Clone, Copy)]
44pub(crate) struct ScrollEvent {
45 pub scroll: Scroll,
47 pub point: DevicePoint,
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: PaintHitTestResult,
64 pub external_scroll_id: ExternalScrollId,
69 pub offset: LayoutVector2D,
70}
71
72#[derive(Debug, PartialEq)]
73pub(crate) enum PinchZoomResult {
74 DidPinchZoom,
75 DidNotPinchZoom,
76}
77
78pub(crate) struct WebViewRenderer {
82 pub id: WebViewId,
84 pub webview: Box<dyn WebViewTrait>,
88 pub root_pipeline_id: Option<PipelineId>,
90 pub rect: DeviceRect,
92 pub pipelines: FxHashMap<PipelineId, PipelineDetails>,
94 pending_scroll_zoom_events: Vec<ScrollZoomEvent>,
96 pending_wheel_events: FxHashMap<InputEventId, WheelEvent>,
100 touch_handler: TouchHandler,
102 pub page_zoom: Scale<f32, CSSPixel, DeviceIndependentPixel>,
104 pinch_zoom: PinchZoom,
107 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
110 hidden: bool,
112 animating: bool,
115 viewport_description: Option<ViewportDescription>,
118
119 embedder_to_constellation_sender: Sender<EmbedderToConstellationMessage>,
124 refresh_driver: Rc<BaseRefreshDriver>,
126 webrender_document: DocumentId,
128}
129
130impl WebViewRenderer {
131 pub(crate) fn new(
132 renderer_webview: Box<dyn WebViewTrait>,
133 viewport_details: ViewportDetails,
134 embedder_to_constellation_sender: Sender<EmbedderToConstellationMessage>,
135 refresh_driver: Rc<BaseRefreshDriver>,
136 webrender_document: DocumentId,
137 ) -> Self {
138 let hidpi_scale_factor = viewport_details.hidpi_scale_factor;
139 let size = viewport_details.size * viewport_details.hidpi_scale_factor;
140 let rect = DeviceRect::from_origin_and_size(DevicePoint::origin(), size);
141 let webview_id = renderer_webview.id();
142 Self {
143 id: webview_id,
144 webview: renderer_webview,
145 root_pipeline_id: None,
146 rect,
147 pipelines: Default::default(),
148 touch_handler: TouchHandler::new(webview_id),
149 pending_scroll_zoom_events: Default::default(),
150 pending_wheel_events: Default::default(),
151 page_zoom: DEFAULT_PAGE_ZOOM,
152 pinch_zoom: PinchZoom::new(rect),
153 hidpi_scale_factor: Scale::new(hidpi_scale_factor.0),
154 hidden: false,
155 animating: false,
156 viewport_description: None,
157 embedder_to_constellation_sender,
158 refresh_driver,
159 webrender_document,
160 }
161 }
162
163 fn hit_test(&self, webrender_api: &RenderApi, point: DevicePoint) -> Vec<PaintHitTestResult> {
164 Painter::hit_test_at_point_with_api_and_document(
165 webrender_api,
166 self.webrender_document,
167 point,
168 )
169 }
170
171 pub(crate) fn animation_callbacks_running(&self) -> bool {
172 self.pipelines
173 .values()
174 .any(PipelineDetails::animation_callbacks_running)
175 }
176
177 pub(crate) fn animating(&self) -> bool {
178 self.animating
179 }
180
181 pub(crate) fn hidden(&self) -> bool {
182 self.hidden
183 }
184
185 pub(crate) fn set_hidden(&mut self, new_value: bool) -> bool {
188 let old_value = std::mem::replace(&mut self.hidden, new_value);
189 new_value != old_value
190 }
191
192 pub(crate) fn ensure_pipeline_details(
194 &mut self,
195 pipeline_id: PipelineId,
196 ) -> &mut PipelineDetails {
197 self.pipelines
198 .entry(pipeline_id)
199 .or_insert_with(PipelineDetails::new)
200 }
201
202 pub(crate) fn pipeline_exited(&mut self, pipeline_id: PipelineId, source: PipelineExitSource) {
203 let pipeline = self.pipelines.entry(pipeline_id);
204 let Entry::Occupied(mut pipeline) = pipeline else {
205 return;
206 };
207
208 pipeline.get_mut().exited.insert(source);
209
210 if !pipeline.get().exited.is_all() {
214 return;
215 }
216
217 pipeline.remove_entry();
218 }
219
220 pub(crate) fn set_frame_tree(&mut self, frame_tree: &SendableFrameTree) {
221 let pipeline_id = frame_tree.pipeline.id;
222 let old_pipeline_id = self.root_pipeline_id.replace(pipeline_id);
223
224 if old_pipeline_id != self.root_pipeline_id {
225 debug!(
226 "Updating webview ({:?}) from pipeline {:?} to {:?}",
227 3, old_pipeline_id, self.root_pipeline_id
228 );
229 }
230
231 self.set_frame_tree_on_pipeline_details(frame_tree, None);
232 }
233
234 pub(crate) fn send_scroll_positions_to_layout_for_pipeline(
235 &self,
236 pipeline_id: PipelineId,
237 scrolled_node: ExternalScrollId,
238 ) {
239 let Some(details) = self.pipelines.get(&pipeline_id) else {
240 return;
241 };
242
243 let offsets = details.scroll_tree.scroll_offsets();
244
245 if offsets.is_empty() {
250 return;
251 }
252
253 let _ = self.embedder_to_constellation_sender.send(
254 EmbedderToConstellationMessage::SetScrollStates(
255 pipeline_id,
256 ScrollStateUpdate {
257 scrolled_node,
258 offsets,
259 },
260 ),
261 );
262 }
263
264 pub(crate) fn set_frame_tree_on_pipeline_details(
265 &mut self,
266 frame_tree: &SendableFrameTree,
267 parent_pipeline_id: Option<PipelineId>,
268 ) {
269 let pipeline_id = frame_tree.pipeline.id;
270 let pipeline_details = self.ensure_pipeline_details(pipeline_id);
271 pipeline_details.pipeline = Some(frame_tree.pipeline.clone());
272 pipeline_details.parent_pipeline_id = parent_pipeline_id;
273 pipeline_details.children = frame_tree
274 .children
275 .iter()
276 .map(|frame_tree| frame_tree.pipeline.id)
277 .collect();
278
279 for kid in &frame_tree.children {
280 self.set_frame_tree_on_pipeline_details(kid, Some(pipeline_id));
281 }
282 }
283
284 pub(crate) fn change_pipeline_running_animations_state(
287 &mut self,
288 pipeline_id: PipelineId,
289 animation_state: AnimationState,
290 ) -> bool {
291 let pipeline_details = self.ensure_pipeline_details(pipeline_id);
292 let was_animating = pipeline_details.animating();
293 match animation_state {
294 AnimationState::AnimationsPresent => {
295 pipeline_details.animations_running = true;
296 },
297 AnimationState::AnimationCallbacksPresent => {
298 pipeline_details.animation_callbacks_running = true;
299 },
300 AnimationState::NoAnimationsPresent => {
301 pipeline_details.animations_running = false;
302 },
303 AnimationState::NoAnimationCallbacksPresent => {
304 pipeline_details.animation_callbacks_running = false;
305 },
306 }
307 let started_animating = !was_animating && pipeline_details.animating();
308
309 self.update_animation_state();
310
311 started_animating
316 }
317
318 pub(crate) fn set_throttled(&mut self, pipeline_id: PipelineId, throttled: bool) -> bool {
321 let pipeline_details = self.ensure_pipeline_details(pipeline_id);
322 let was_animating = pipeline_details.animating();
323 pipeline_details.throttled = throttled;
324 let started_animating = !was_animating && pipeline_details.animating();
325
326 self.update_animation_state();
328
329 started_animating
334 }
335
336 fn update_animation_state(&mut self) {
337 self.animating = self.pipelines.values().any(PipelineDetails::animating);
338 self.webview.set_animating(self.animating());
339 }
340
341 pub(crate) fn for_each_connected_pipeline(&self, callback: &mut impl FnMut(&PipelineDetails)) {
342 if let Some(root_pipeline_id) = self.root_pipeline_id {
343 self.for_each_connected_pipeline_internal(root_pipeline_id, callback);
344 }
345 }
346
347 fn for_each_connected_pipeline_internal(
348 &self,
349 pipeline_id: PipelineId,
350 callback: &mut impl FnMut(&PipelineDetails),
351 ) {
352 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
353 return;
354 };
355 callback(pipeline);
356 for child_pipeline_id in &pipeline.children {
357 self.for_each_connected_pipeline_internal(*child_pipeline_id, callback);
358 }
359 }
360
361 pub(crate) fn update_touch_handling_at_new_frame_start(&mut self) -> bool {
365 let Some(fling_action) = self.touch_handler.notify_new_frame_start() else {
366 return false;
367 };
368
369 self.on_scroll_window_event(
370 Scroll::Delta((-fling_action.delta).into()),
371 fling_action.cursor,
372 );
373 true
374 }
375
376 fn dispatch_input_event_with_hit_testing(
377 &mut self,
378 render_api: &RenderApi,
379 event: InputEventAndId,
380 ) -> bool {
381 let event_point = event
382 .event
383 .point()
384 .map(|point| point.as_device_point(self.device_pixels_per_page_pixel()));
385 let hit_test_result = match event_point {
386 Some(point) => {
387 let hit_test_result = match event.event {
388 InputEvent::Touch(_) => self.touch_handler.get_hit_test_result_cache_value(),
389 _ => None,
390 }
391 .or_else(|| self.hit_test(render_api, point).into_iter().nth(0));
392 if hit_test_result.is_none() {
393 warn!("Empty hit test result for input event, ignoring.");
394 return false;
395 }
396 hit_test_result
397 },
398 None => None,
399 };
400
401 if let Err(error) = self.embedder_to_constellation_sender.send(
402 EmbedderToConstellationMessage::ForwardInputEvent(self.id, event, hit_test_result),
403 ) {
404 warn!("Sending event to constellation failed ({error:?}).");
405 false
406 } else {
407 true
408 }
409 }
410
411 pub(crate) fn notify_input_event(
412 &mut self,
413 render_api: &RenderApi,
414 repaint_reason: &Cell<RepaintReason>,
415 event_and_id: InputEventAndId,
416 ) -> bool {
417 if let InputEvent::Touch(touch_event) = event_and_id.event {
418 return self.on_touch_event(render_api, repaint_reason, touch_event, event_and_id.id);
419 }
420
421 if let InputEvent::Wheel(wheel_event) = event_and_id.event {
422 self.pending_wheel_events
423 .insert(event_and_id.id, wheel_event);
424 }
425
426 self.dispatch_input_event_with_hit_testing(render_api, event_and_id)
427 }
428
429 fn send_touch_event(
430 &mut self,
431 render_api: &RenderApi,
432 event: TouchEvent,
433 id: InputEventId,
434 ) -> bool {
435 let cancelable = event.is_cancelable();
436 let event_type = event.event_type;
437
438 let input_event_and_id = InputEventAndId {
439 event: InputEvent::Touch(event),
440 id,
441 };
442
443 let result = self.dispatch_input_event_with_hit_testing(render_api, input_event_and_id);
444
445 if cancelable && result {
449 self.touch_handler
450 .add_pending_touch_input_event(id, event.touch_id, event_type);
451 }
452
453 result
454 }
455
456 pub(crate) fn on_touch_event(
457 &mut self,
458 render_api: &RenderApi,
459 repaint_reason: &Cell<RepaintReason>,
460 event: TouchEvent,
461 id: InputEventId,
462 ) -> bool {
463 let result = match event.event_type {
464 TouchEventType::Down => self.on_touch_down(render_api, event, id),
465 TouchEventType::Move => self.on_touch_move(render_api, event, id),
466 TouchEventType::Up => self.on_touch_up(render_api, event, id),
467 TouchEventType::Cancel => self.on_touch_cancel(render_api, event, id),
468 };
469
470 self.touch_handler
471 .add_touch_move_refresh_observer_if_necessary(
472 self.refresh_driver.clone(),
473 repaint_reason,
474 );
475 result
476 }
477
478 fn on_touch_down(
479 &mut self,
480 render_api: &RenderApi,
481 event: TouchEvent,
482 id: InputEventId,
483 ) -> bool {
484 let point = event
485 .point
486 .as_device_point(self.device_pixels_per_page_pixel());
487 self.touch_handler.on_touch_down(event.touch_id, point);
488 self.send_touch_event(render_api, event, id)
489 }
490
491 fn on_touch_move(
492 &mut self,
493 render_api: &RenderApi,
494 mut event: TouchEvent,
495 id: InputEventId,
496 ) -> bool {
497 let point = event
498 .point
499 .as_device_point(self.device_pixels_per_page_pixel());
500 let action = self.touch_handler.on_touch_move(
501 event.touch_id,
502 point,
503 self.device_pixels_per_page_pixel_not_including_pinch_zoom()
504 .get(),
505 );
506 if let Some(action) = action {
507 if self
510 .touch_handler
511 .move_allowed(self.touch_handler.current_sequence_id)
512 {
513 event.disable_cancelable();
515 self.pending_scroll_zoom_events.push(action);
516 }
517 }
518 let mut reached_constellation = false;
519 if !self.touch_handler.is_handling_touch_move_for_touch_id(
523 self.touch_handler.current_sequence_id,
524 event.touch_id,
525 ) {
526 reached_constellation = self.send_touch_event(render_api, event, id);
527 if reached_constellation && event.is_cancelable() {
528 self.touch_handler.set_handling_touch_move_for_touch_id(
529 self.touch_handler.current_sequence_id,
530 event.touch_id,
531 TouchIdMoveTracking::Track,
532 );
533 }
534 }
535 reached_constellation
536 }
537
538 fn on_touch_up(&mut self, render_api: &RenderApi, event: TouchEvent, id: InputEventId) -> bool {
539 let point = event
540 .point
541 .as_device_point(self.device_pixels_per_page_pixel());
542 self.touch_handler.on_touch_up(event.touch_id, point);
543 self.send_touch_event(render_api, event, id)
544 }
545
546 fn on_touch_cancel(
547 &mut self,
548 render_api: &RenderApi,
549 event: TouchEvent,
550 id: InputEventId,
551 ) -> bool {
552 let point = event
553 .point
554 .as_device_point(self.device_pixels_per_page_pixel());
555 self.touch_handler.on_touch_cancel(event.touch_id, point);
556 self.send_touch_event(render_api, event, id)
557 }
558
559 fn on_touch_event_processed(
560 &mut self,
561 render_api: &RenderApi,
562 pending_touch_input_event: PendingTouchInputEvent,
563 result: InputEventResult,
564 ) {
565 let PendingTouchInputEvent {
566 sequence_id,
567 event_type,
568 touch_id,
569 } = pending_touch_input_event;
570
571 if result.contains(InputEventResult::DefaultPrevented) {
572 debug!(
573 "Touch event {:?} in sequence {:?} prevented!",
574 event_type, sequence_id
575 );
576 match event_type {
577 TouchEventType::Down => {
578 self.touch_handler.prevent_click(sequence_id);
580 self.touch_handler.prevent_move(sequence_id);
581 self.touch_handler
582 .remove_pending_touch_move_actions(sequence_id);
583 },
584 TouchEventType::Move => {
585 if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) {
587 info.prevent_move = TouchMoveAllowed::Prevented;
588 if let TouchSequenceState::PendingFling { .. } = info.state {
589 info.state = TouchSequenceState::Finished;
590 }
591 self.touch_handler.set_handling_touch_move_for_touch_id(
592 self.touch_handler.current_sequence_id,
593 touch_id,
594 TouchIdMoveTracking::Remove,
595 );
596 self.touch_handler
597 .remove_pending_touch_move_actions(sequence_id);
598 }
599 },
600 TouchEventType::Up => {
601 let Some(info) = &mut self.touch_handler.get_touch_sequence_mut(sequence_id)
605 else {
606 return;
611 };
612 match info.state {
613 TouchSequenceState::PendingClick(_) => {
614 info.state = TouchSequenceState::Finished;
615 self.touch_handler.remove_touch_sequence(sequence_id);
616 },
617 TouchSequenceState::Flinging { .. } => {
618 },
620 TouchSequenceState::Finished => {
621 self.touch_handler.remove_touch_sequence(sequence_id);
622 },
623 TouchSequenceState::Touching |
624 TouchSequenceState::Panning { .. } |
625 TouchSequenceState::Pinching |
626 TouchSequenceState::MultiTouch |
627 TouchSequenceState::PendingFling { .. } => {
628 },
633 }
634 },
635 TouchEventType::Cancel => {
636 self.touch_handler
639 .remove_pending_touch_move_actions(sequence_id);
640 self.touch_handler.try_remove_touch_sequence(sequence_id);
641 },
642 }
643 } else {
644 debug!(
645 "Touch event {:?} in sequence {:?} allowed",
646 event_type, sequence_id
647 );
648 match event_type {
649 TouchEventType::Down => {},
650 TouchEventType::Move => {
651 self.pending_scroll_zoom_events.extend(
652 self.touch_handler
653 .take_pending_touch_move_actions(sequence_id),
654 );
655 self.touch_handler.set_handling_touch_move_for_touch_id(
656 self.touch_handler.current_sequence_id,
657 touch_id,
658 TouchIdMoveTracking::Remove,
659 );
660 if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) {
661 if info.prevent_move == TouchMoveAllowed::Pending {
662 info.prevent_move = TouchMoveAllowed::Allowed;
663 if let TouchSequenceState::PendingFling { velocity, point } = info.state
664 {
665 info.state = TouchSequenceState::Flinging { velocity, point }
666 }
667 }
668 }
669 },
670 TouchEventType::Up => {
671 let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) else {
672 return;
674 };
675 match info.state {
676 TouchSequenceState::PendingClick(point) => {
677 info.state = TouchSequenceState::Finished;
678 if !info.prevent_click {
681 self.simulate_mouse_click(render_api, point);
682 }
683 self.touch_handler.remove_touch_sequence(sequence_id);
684 },
685 TouchSequenceState::Flinging { .. } => {
686 },
688 TouchSequenceState::Finished => {
689 self.touch_handler.remove_touch_sequence(sequence_id);
690 },
691 TouchSequenceState::Panning { .. } |
692 TouchSequenceState::Pinching |
693 TouchSequenceState::PendingFling { .. } => {
694 },
699 TouchSequenceState::MultiTouch | TouchSequenceState::Touching => {
700 },
702 }
703 },
704 TouchEventType::Cancel => {
705 self.touch_handler
706 .remove_pending_touch_move_actions(sequence_id);
707 self.touch_handler.try_remove_touch_sequence(sequence_id);
708 },
709 }
710 }
711 }
712
713 fn simulate_mouse_click(&mut self, render_api: &RenderApi, point: DevicePoint) {
715 let button = MouseButton::Left;
716 self.dispatch_input_event_with_hit_testing(
717 render_api,
718 InputEvent::MouseMove(MouseMoveEvent::new_compatibility_for_touch(point.into())).into(),
719 );
720 self.dispatch_input_event_with_hit_testing(
721 render_api,
722 InputEvent::MouseButton(MouseButtonEvent::new(
723 MouseButtonAction::Down,
724 button,
725 point.into(),
726 ))
727 .into(),
728 );
729 self.dispatch_input_event_with_hit_testing(
730 render_api,
731 InputEvent::MouseButton(MouseButtonEvent::new(
732 MouseButtonAction::Up,
733 button,
734 point.into(),
735 ))
736 .into(),
737 );
738 }
739
740 pub(crate) fn notify_scroll_event(&mut self, scroll: Scroll, point: WebViewPoint) {
741 let point = point.as_device_point(self.device_pixels_per_page_pixel());
742 self.on_scroll_window_event(scroll, point);
743 }
744
745 fn on_scroll_window_event(&mut self, scroll: Scroll, cursor: DevicePoint) {
746 self.pending_scroll_zoom_events
747 .push(ScrollZoomEvent::Scroll(ScrollEvent {
748 scroll,
749 point: cursor,
750 }));
751 }
752
753 pub(crate) fn process_pending_scroll_and_pinch_zoom_events(
760 &mut self,
761 render_api: &RenderApi,
762 ) -> (PinchZoomResult, Option<ScrollResult>) {
763 if self.pending_scroll_zoom_events.is_empty() {
764 return (PinchZoomResult::DidNotPinchZoom, None);
765 }
766
767 let mut combined_scroll_event: Option<ScrollEvent> = None;
770 let mut new_pinch_zoom = self.pinch_zoom;
771 let device_pixels_per_page_pixel = self.device_pixels_per_page_pixel();
772
773 for scroll_event in self.pending_scroll_zoom_events.drain(..) {
774 match scroll_event {
775 ScrollZoomEvent::PinchZoom(factor, center) => {
776 new_pinch_zoom.zoom(factor, center);
777 },
778 ScrollZoomEvent::Scroll(scroll_event_info) => {
779 let combined_event = match combined_scroll_event.as_mut() {
780 None => {
781 combined_scroll_event = Some(scroll_event_info);
782 continue;
783 },
784 Some(combined_event) => combined_event,
785 };
786
787 match (combined_event.scroll, scroll_event_info.scroll) {
788 (Scroll::Delta(old_delta), Scroll::Delta(new_delta)) => {
789 let old_delta =
790 old_delta.as_device_vector(device_pixels_per_page_pixel);
791 let new_delta =
792 new_delta.as_device_vector(device_pixels_per_page_pixel);
793 combined_event.scroll = Scroll::Delta((old_delta + new_delta).into());
794 },
795 (Scroll::Start, _) | (Scroll::End, _) => {
796 break;
798 },
799 (_, Scroll::Start) | (_, Scroll::End) => {
800 *combined_event = scroll_event_info;
803 break;
804 },
805 }
806 },
807 }
808 }
809
810 if let Some(combined_scroll_event) = combined_scroll_event.as_mut() {
814 new_pinch_zoom.pan(
815 &mut combined_scroll_event.scroll,
816 self.device_pixels_per_page_pixel(),
817 )
818 }
819
820 let scroll_result = combined_scroll_event.and_then(|combined_event| {
821 self.scroll_node_at_device_point(
822 render_api,
823 combined_event.point.to_f32(),
824 combined_event.scroll,
825 )
826 });
827 if let Some(ref scroll_result) = scroll_result {
828 self.send_scroll_positions_to_layout_for_pipeline(
829 scroll_result.hit_test_result.pipeline_id,
830 scroll_result.external_scroll_id,
831 );
832 } else {
833 self.touch_handler.stop_fling_if_needed();
834 }
835
836 let pinch_zoom_result = self.set_pinch_zoom(new_pinch_zoom);
838 if pinch_zoom_result == PinchZoomResult::DidPinchZoom {
839 self.send_pinch_zoom_infos_to_script();
840 }
841
842 (pinch_zoom_result, scroll_result)
843 }
844
845 fn scroll_node_at_device_point(
850 &mut self,
851 render_api: &RenderApi,
852 cursor: DevicePoint,
853 scroll: Scroll,
854 ) -> Option<ScrollResult> {
855 let scroll_location = match scroll {
856 Scroll::Delta(delta) => {
857 let device_pixels_per_page = self.device_pixels_per_page_pixel();
858 let calculate_delta =
859 delta.as_device_vector(device_pixels_per_page) / device_pixels_per_page;
860 ScrollLocation::Delta(calculate_delta.cast_unit())
861 },
862 Scroll::Start => ScrollLocation::Start,
863 Scroll::End => ScrollLocation::End,
864 };
865
866 let hit_test_results: Vec<_> = self
867 .touch_handler
868 .get_hit_test_result_cache_value()
869 .map(|result| vec![result])
870 .unwrap_or_else(|| self.hit_test(render_api, cursor));
871
872 let mut previous_pipeline_id = None;
876 for hit_test_result in hit_test_results {
877 let pipeline_details = self.pipelines.get_mut(&hit_test_result.pipeline_id)?;
878 if previous_pipeline_id.replace(hit_test_result.pipeline_id) !=
879 Some(hit_test_result.pipeline_id)
880 {
881 let scroll_result = pipeline_details.scroll_tree.scroll_node_or_ancestor(
882 hit_test_result.external_scroll_id,
883 scroll_location,
884 ScrollType::InputEvents,
885 );
886 if let Some((external_scroll_id, offset)) = scroll_result {
887 self.touch_handler.set_hit_test_result_cache_value(
893 hit_test_result.clone(),
894 self.device_pixels_per_page_pixel(),
895 );
896 return Some(ScrollResult {
897 hit_test_result,
898 external_scroll_id,
899 offset,
900 });
901 }
902 }
903 }
904 None
905 }
906
907 pub(crate) fn scroll_viewport_by_delta(
911 &mut self,
912 delta: LayoutVector2D,
913 ) -> (PinchZoomResult, Vec<ScrollResult>) {
914 let device_pixels_per_page_pixel = self.device_pixels_per_page_pixel();
915 let delta_in_device_pixels = delta.cast_unit() * device_pixels_per_page_pixel;
916 let remaining = self.pinch_zoom.pan_with_device_scroll(
917 Scroll::Delta(delta_in_device_pixels.into()),
918 device_pixels_per_page_pixel,
919 );
920
921 let pinch_zoom_result = match remaining == delta_in_device_pixels {
922 true => PinchZoomResult::DidNotPinchZoom,
923 false => PinchZoomResult::DidPinchZoom,
924 };
925 if remaining == Vector2D::zero() {
926 return (pinch_zoom_result, vec![]);
927 }
928
929 let Some(root_pipeline_id) = self.root_pipeline_id else {
930 return (pinch_zoom_result, vec![]);
931 };
932 let Some(root_pipeline) = self.pipelines.get_mut(&root_pipeline_id) else {
933 return (pinch_zoom_result, vec![]);
934 };
935
936 let remaining = remaining / device_pixels_per_page_pixel;
937 let Some((external_scroll_id, offset)) = root_pipeline.scroll_tree.scroll_node_or_ancestor(
938 ExternalScrollId(0, root_pipeline_id.into()),
939 ScrollLocation::Delta(remaining.cast_unit()),
940 ScrollType::InputEvents,
942 ) else {
943 return (pinch_zoom_result, vec![]);
944 };
945
946 let hit_test_result = PaintHitTestResult {
947 pipeline_id: root_pipeline_id,
948 point_in_viewport: Default::default(),
951 external_scroll_id,
952 };
953
954 self.send_scroll_positions_to_layout_for_pipeline(root_pipeline_id, external_scroll_id);
955
956 if pinch_zoom_result == PinchZoomResult::DidPinchZoom {
957 self.send_pinch_zoom_infos_to_script();
958 }
959
960 let scroll_result = ScrollResult {
961 hit_test_result,
962 external_scroll_id,
963 offset,
964 };
965 (pinch_zoom_result, vec![scroll_result])
966 }
967
968 fn send_pinch_zoom_infos_to_script(&self) {
970 let Some(pipeline_id) = self.root_pipeline_id else {
972 return;
973 };
974
975 let pinch_zoom_infos = self.pinch_zoom.get_pinch_zoom_infos_for_script(
976 self.device_pixels_per_page_pixel_not_including_pinch_zoom(),
977 );
978
979 let _ = self.embedder_to_constellation_sender.send(
980 EmbedderToConstellationMessage::UpdatePinchZoomInfos(pipeline_id, pinch_zoom_infos),
981 );
982 }
983
984 pub(crate) fn pinch_zoom(&self) -> PinchZoom {
985 self.pinch_zoom
986 }
987
988 fn set_pinch_zoom(&mut self, requested_pinch_zoom: PinchZoom) -> PinchZoomResult {
989 if requested_pinch_zoom == self.pinch_zoom {
990 return PinchZoomResult::DidNotPinchZoom;
991 }
992
993 self.pinch_zoom = requested_pinch_zoom;
994 PinchZoomResult::DidPinchZoom
995 }
996
997 pub(crate) fn set_page_zoom(
998 &mut self,
999 new_page_zoom: Scale<f32, CSSPixel, DeviceIndependentPixel>,
1000 ) {
1001 let new_page_zoom = new_page_zoom.clamp(MIN_PAGE_ZOOM, MAX_PAGE_ZOOM);
1002 let old_zoom = std::mem::replace(&mut self.page_zoom, new_page_zoom);
1003 if old_zoom != self.page_zoom {
1004 self.send_window_size_message();
1005 }
1006 }
1007
1008 pub(crate) fn device_pixels_per_page_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> {
1014 let viewport_scale = self
1015 .root_pipeline_id
1016 .and_then(|pipeline_id| self.pipelines.get(&pipeline_id))
1017 .and_then(|pipeline| pipeline.viewport_scale)
1018 .unwrap_or_else(|| self.page_zoom * self.hidpi_scale_factor);
1019 viewport_scale * self.pinch_zoom.zoom_factor()
1020 }
1021
1022 pub(crate) fn device_pixels_per_page_pixel_not_including_pinch_zoom(
1027 &self,
1028 ) -> Scale<f32, CSSPixel, DevicePixel> {
1029 self.page_zoom * self.hidpi_scale_factor
1030 }
1031
1032 pub(crate) fn adjust_pinch_zoom(&mut self, magnification: f32, center: DevicePoint) {
1034 if magnification == 1.0 {
1035 return;
1036 }
1037
1038 self.pending_scroll_zoom_events
1039 .push(ScrollZoomEvent::PinchZoom(magnification, center));
1040 }
1041
1042 fn send_window_size_message(&self) {
1043 let device_pixel_ratio = self.device_pixels_per_page_pixel_not_including_pinch_zoom();
1046 let initial_viewport = self.rect.size().to_f32() / device_pixel_ratio;
1047 let _ = self.embedder_to_constellation_sender.send(
1048 EmbedderToConstellationMessage::ChangeViewportDetails(
1049 self.id,
1050 ViewportDetails {
1051 hidpi_scale_factor: device_pixel_ratio,
1052 size: initial_viewport,
1053 },
1054 WindowSizeType::Resize,
1055 ),
1056 );
1057 }
1058
1059 pub(crate) fn set_hidpi_scale_factor(
1061 &mut self,
1062 new_scale: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1063 ) -> bool {
1064 let old_scale_factor = std::mem::replace(&mut self.hidpi_scale_factor, new_scale);
1065 if self.hidpi_scale_factor == old_scale_factor {
1066 return false;
1067 }
1068
1069 self.send_window_size_message();
1070 true
1071 }
1072
1073 pub(crate) fn set_rect(&mut self, new_rect: DeviceRect) -> bool {
1075 let old_rect = std::mem::replace(&mut self.rect, new_rect);
1076 if old_rect.size() != self.rect.size() {
1077 self.send_window_size_message();
1078 self.pinch_zoom.resize_unscaled_viewport(new_rect);
1079 self.send_pinch_zoom_infos_to_script();
1080 }
1081 old_rect != self.rect
1082 }
1083
1084 pub fn set_viewport_description(&mut self, viewport_description: ViewportDescription) {
1085 self.set_page_zoom(viewport_description.initial_scale);
1086 self.viewport_description = Some(viewport_description);
1087 }
1088
1089 pub(crate) fn scroll_trees_memory_usage(
1090 &self,
1091 ops: &mut malloc_size_of::MallocSizeOfOps,
1092 ) -> usize {
1093 self.pipelines
1094 .values()
1095 .map(|pipeline| pipeline.scroll_tree.size_of(ops))
1096 .sum::<usize>()
1097 }
1098
1099 pub(crate) fn notify_input_event_handled(
1100 &mut self,
1101 render_api: &RenderApi,
1102 repaint_reason: &Cell<RepaintReason>,
1103 id: InputEventId,
1104 result: InputEventResult,
1105 ) {
1106 if let Some(pending_touch_input_event) =
1107 self.touch_handler.take_pending_touch_input_event(id)
1108 {
1109 self.on_touch_event_processed(render_api, pending_touch_input_event, result);
1110 self.touch_handler
1111 .add_touch_move_refresh_observer_if_necessary(
1112 self.refresh_driver.clone(),
1113 repaint_reason,
1114 );
1115 }
1116
1117 if let Some(wheel_event) = self.pending_wheel_events.remove(&id) {
1118 if !result.contains(InputEventResult::DefaultPrevented) {
1119 let scroll_delta =
1121 DeviceVector2D::new(-wheel_event.delta.x as f32, -wheel_event.delta.y as f32);
1122 self.notify_scroll_event(Scroll::Delta(scroll_delta.into()), wheel_event.point);
1123 }
1124 }
1125 }
1126}
1127
1128#[derive(Clone, Copy, Debug, PartialEq)]
1129pub struct UnknownWebView(pub WebViewId);