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: 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: Default::default(),
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 info.prevent_move == TouchMoveAllowed::Pending
662 {
663 info.prevent_move = TouchMoveAllowed::Allowed;
664 if let TouchSequenceState::PendingFling { velocity, point } = info.state {
665 info.state = TouchSequenceState::Flinging { velocity, point }
666 }
667 }
668 },
669 TouchEventType::Up => {
670 let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) else {
671 return;
673 };
674 match info.state {
675 TouchSequenceState::PendingClick(point) => {
676 info.state = TouchSequenceState::Finished;
677 if !info.prevent_click {
680 self.simulate_mouse_click(render_api, point);
681 }
682 self.touch_handler.remove_touch_sequence(sequence_id);
683 },
684 TouchSequenceState::Flinging { .. } => {
685 },
687 TouchSequenceState::Finished => {
688 self.touch_handler.remove_touch_sequence(sequence_id);
689 },
690 TouchSequenceState::Panning { .. } |
691 TouchSequenceState::Pinching |
692 TouchSequenceState::PendingFling { .. } => {
693 },
698 TouchSequenceState::MultiTouch | TouchSequenceState::Touching => {
699 },
701 }
702 },
703 TouchEventType::Cancel => {
704 self.touch_handler
705 .remove_pending_touch_move_actions(sequence_id);
706 self.touch_handler.try_remove_touch_sequence(sequence_id);
707 },
708 }
709 }
710 }
711
712 fn simulate_mouse_click(&mut self, render_api: &RenderApi, point: DevicePoint) {
714 let button = MouseButton::Left;
715 self.dispatch_input_event_with_hit_testing(
716 render_api,
717 InputEvent::MouseMove(MouseMoveEvent::new_compatibility_for_touch(point.into())).into(),
718 );
719 self.dispatch_input_event_with_hit_testing(
720 render_api,
721 InputEvent::MouseButton(MouseButtonEvent::new(
722 MouseButtonAction::Down,
723 button,
724 point.into(),
725 ))
726 .into(),
727 );
728 self.dispatch_input_event_with_hit_testing(
729 render_api,
730 InputEvent::MouseButton(MouseButtonEvent::new(
731 MouseButtonAction::Up,
732 button,
733 point.into(),
734 ))
735 .into(),
736 );
737 }
738
739 pub(crate) fn notify_scroll_event(&mut self, scroll: Scroll, point: WebViewPoint) {
740 let point = point.as_device_point(self.device_pixels_per_page_pixel());
741 self.on_scroll_window_event(scroll, point);
742 }
743
744 fn on_scroll_window_event(&mut self, scroll: Scroll, cursor: DevicePoint) {
745 self.pending_scroll_zoom_events
746 .push(ScrollZoomEvent::Scroll(ScrollEvent {
747 scroll,
748 point: cursor,
749 }));
750 }
751
752 pub(crate) fn process_pending_scroll_and_pinch_zoom_events(
759 &mut self,
760 render_api: &RenderApi,
761 ) -> (PinchZoomResult, Option<ScrollResult>) {
762 if self.pending_scroll_zoom_events.is_empty() {
763 return (PinchZoomResult::DidNotPinchZoom, None);
764 }
765
766 let mut combined_scroll_event: Option<ScrollEvent> = None;
769 let mut new_pinch_zoom = self.pinch_zoom;
770 let device_pixels_per_page_pixel = self.device_pixels_per_page_pixel();
771
772 for scroll_event in self.pending_scroll_zoom_events.drain(..) {
773 match scroll_event {
774 ScrollZoomEvent::PinchZoom(magnification, center) => {
775 let new_factor = self
776 .viewport_description
777 .clamp_zoom(self.pinch_zoom.zoom_factor().0 * magnification);
778 new_pinch_zoom.set_zoom(new_factor, center);
779 },
780 ScrollZoomEvent::Scroll(scroll_event_info) => {
781 let combined_event = match combined_scroll_event.as_mut() {
782 None => {
783 combined_scroll_event = Some(scroll_event_info);
784 continue;
785 },
786 Some(combined_event) => combined_event,
787 };
788
789 match (combined_event.scroll, scroll_event_info.scroll) {
790 (Scroll::Delta(old_delta), Scroll::Delta(new_delta)) => {
791 let old_delta =
792 old_delta.as_device_vector(device_pixels_per_page_pixel);
793 let new_delta =
794 new_delta.as_device_vector(device_pixels_per_page_pixel);
795 combined_event.scroll = Scroll::Delta((old_delta + new_delta).into());
796 },
797 (Scroll::Start, _) | (Scroll::End, _) => {
798 break;
800 },
801 (_, Scroll::Start) | (_, Scroll::End) => {
802 *combined_event = scroll_event_info;
805 break;
806 },
807 }
808 },
809 }
810 }
811
812 if let Some(combined_scroll_event) = combined_scroll_event.as_mut() {
816 new_pinch_zoom.pan(
817 &mut combined_scroll_event.scroll,
818 self.device_pixels_per_page_pixel(),
819 )
820 }
821
822 let scroll_result = combined_scroll_event.and_then(|combined_event| {
823 self.scroll_node_at_device_point(
824 render_api,
825 combined_event.point.to_f32(),
826 combined_event.scroll,
827 )
828 });
829 if let Some(ref scroll_result) = scroll_result {
830 self.send_scroll_positions_to_layout_for_pipeline(
831 scroll_result.hit_test_result.pipeline_id,
832 scroll_result.external_scroll_id,
833 );
834 } else {
835 self.touch_handler.stop_fling_if_needed();
836 }
837
838 let pinch_zoom_result = self.set_pinch_zoom(new_pinch_zoom);
840 if pinch_zoom_result == PinchZoomResult::DidPinchZoom {
841 self.send_pinch_zoom_infos_to_script();
842 }
843
844 (pinch_zoom_result, scroll_result)
845 }
846
847 fn scroll_node_at_device_point(
852 &mut self,
853 render_api: &RenderApi,
854 cursor: DevicePoint,
855 scroll: Scroll,
856 ) -> Option<ScrollResult> {
857 let scroll_location = match scroll {
858 Scroll::Delta(delta) => {
859 let device_pixels_per_page = self.device_pixels_per_page_pixel();
860 let calculate_delta =
861 delta.as_device_vector(device_pixels_per_page) / device_pixels_per_page;
862 ScrollLocation::Delta(calculate_delta.cast_unit())
863 },
864 Scroll::Start => ScrollLocation::Start,
865 Scroll::End => ScrollLocation::End,
866 };
867
868 let hit_test_results: Vec<_> = self
869 .touch_handler
870 .get_hit_test_result_cache_value()
871 .map(|result| vec![result])
872 .unwrap_or_else(|| self.hit_test(render_api, cursor));
873
874 let mut previous_pipeline_id = None;
878 for hit_test_result in hit_test_results {
879 let pipeline_details = self.pipelines.get_mut(&hit_test_result.pipeline_id)?;
880 if previous_pipeline_id.replace(hit_test_result.pipeline_id) !=
881 Some(hit_test_result.pipeline_id)
882 {
883 let scroll_result = pipeline_details.scroll_tree.scroll_node_or_ancestor(
884 hit_test_result.external_scroll_id,
885 scroll_location,
886 ScrollType::InputEvents,
887 );
888 if let Some((external_scroll_id, offset)) = scroll_result {
889 self.touch_handler.set_hit_test_result_cache_value(
895 hit_test_result.clone(),
896 self.device_pixels_per_page_pixel(),
897 );
898 return Some(ScrollResult {
899 hit_test_result,
900 external_scroll_id,
901 offset,
902 });
903 }
904 }
905 }
906 None
907 }
908
909 pub(crate) fn scroll_viewport_by_delta(
913 &mut self,
914 delta: LayoutVector2D,
915 ) -> (PinchZoomResult, Vec<ScrollResult>) {
916 let device_pixels_per_page_pixel = self.device_pixels_per_page_pixel();
917 let delta_in_device_pixels = delta.cast_unit() * device_pixels_per_page_pixel;
918 let remaining = self.pinch_zoom.pan_with_device_scroll(
919 Scroll::Delta(delta_in_device_pixels.into()),
920 device_pixels_per_page_pixel,
921 );
922
923 let pinch_zoom_result = match remaining == delta_in_device_pixels {
924 true => PinchZoomResult::DidNotPinchZoom,
925 false => PinchZoomResult::DidPinchZoom,
926 };
927 if remaining == Vector2D::zero() {
928 return (pinch_zoom_result, vec![]);
929 }
930
931 let Some(root_pipeline_id) = self.root_pipeline_id else {
932 return (pinch_zoom_result, vec![]);
933 };
934 let Some(root_pipeline) = self.pipelines.get_mut(&root_pipeline_id) else {
935 return (pinch_zoom_result, vec![]);
936 };
937
938 let remaining = remaining / device_pixels_per_page_pixel;
939 let Some((external_scroll_id, offset)) = root_pipeline.scroll_tree.scroll_node_or_ancestor(
940 ExternalScrollId(0, root_pipeline_id.into()),
941 ScrollLocation::Delta(remaining.cast_unit()),
942 ScrollType::InputEvents,
944 ) else {
945 return (pinch_zoom_result, vec![]);
946 };
947
948 let hit_test_result = PaintHitTestResult {
949 pipeline_id: root_pipeline_id,
950 point_in_viewport: Default::default(),
953 external_scroll_id,
954 };
955
956 self.send_scroll_positions_to_layout_for_pipeline(root_pipeline_id, external_scroll_id);
957
958 if pinch_zoom_result == PinchZoomResult::DidPinchZoom {
959 self.send_pinch_zoom_infos_to_script();
960 }
961
962 let scroll_result = ScrollResult {
963 hit_test_result,
964 external_scroll_id,
965 offset,
966 };
967 (pinch_zoom_result, vec![scroll_result])
968 }
969
970 fn send_pinch_zoom_infos_to_script(&self) {
972 let Some(pipeline_id) = self.root_pipeline_id else {
974 return;
975 };
976
977 let pinch_zoom_infos = self.pinch_zoom.get_pinch_zoom_infos_for_script(
978 self.device_pixels_per_page_pixel_not_including_pinch_zoom(),
979 );
980
981 let _ = self.embedder_to_constellation_sender.send(
982 EmbedderToConstellationMessage::UpdatePinchZoomInfos(pipeline_id, pinch_zoom_infos),
983 );
984 }
985
986 pub(crate) fn pinch_zoom(&self) -> PinchZoom {
987 self.pinch_zoom
988 }
989
990 fn set_pinch_zoom(&mut self, requested_pinch_zoom: PinchZoom) -> PinchZoomResult {
991 if requested_pinch_zoom == self.pinch_zoom {
992 return PinchZoomResult::DidNotPinchZoom;
993 }
994
995 self.pinch_zoom = requested_pinch_zoom;
996 PinchZoomResult::DidPinchZoom
997 }
998
999 pub(crate) fn set_page_zoom(
1000 &mut self,
1001 new_page_zoom: Scale<f32, CSSPixel, DeviceIndependentPixel>,
1002 ) {
1003 let new_page_zoom = new_page_zoom.clamp(MIN_PAGE_ZOOM, MAX_PAGE_ZOOM);
1004 let old_zoom = std::mem::replace(&mut self.page_zoom, new_page_zoom);
1005 if old_zoom != self.page_zoom {
1006 self.send_window_size_message();
1007 }
1008 }
1009
1010 pub(crate) fn device_pixels_per_page_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> {
1016 let viewport_scale = self
1017 .root_pipeline_id
1018 .and_then(|pipeline_id| self.pipelines.get(&pipeline_id))
1019 .and_then(|pipeline| pipeline.viewport_scale)
1020 .unwrap_or_else(|| self.page_zoom * self.hidpi_scale_factor);
1021 viewport_scale * self.pinch_zoom.zoom_factor()
1022 }
1023
1024 pub(crate) fn device_pixels_per_page_pixel_not_including_pinch_zoom(
1029 &self,
1030 ) -> Scale<f32, CSSPixel, DevicePixel> {
1031 self.page_zoom * self.hidpi_scale_factor
1032 }
1033
1034 pub(crate) fn adjust_pinch_zoom(&mut self, magnification: f32, center: DevicePoint) {
1036 if magnification == 1.0 {
1037 return;
1038 }
1039
1040 self.pending_scroll_zoom_events
1041 .push(ScrollZoomEvent::PinchZoom(magnification, center));
1042 }
1043
1044 fn send_window_size_message(&self) {
1045 let device_pixel_ratio = self.device_pixels_per_page_pixel_not_including_pinch_zoom();
1048 let layout_viewport = self.rect.size().to_f32() /
1051 (device_pixel_ratio * Scale::new(self.viewport_description.initial_scale.get()));
1052 let _ = self.embedder_to_constellation_sender.send(
1053 EmbedderToConstellationMessage::ChangeViewportDetails(
1054 self.id,
1055 ViewportDetails {
1056 hidpi_scale_factor: device_pixel_ratio,
1057 size: layout_viewport,
1058 },
1059 WindowSizeType::Resize,
1060 ),
1061 );
1062 }
1063
1064 pub(crate) fn set_hidpi_scale_factor(
1066 &mut self,
1067 new_scale: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1068 ) -> bool {
1069 let old_scale_factor = std::mem::replace(&mut self.hidpi_scale_factor, new_scale);
1070 if self.hidpi_scale_factor == old_scale_factor {
1071 return false;
1072 }
1073
1074 self.send_window_size_message();
1075 true
1076 }
1077
1078 pub(crate) fn set_rect(&mut self, new_rect: DeviceRect) -> bool {
1080 let old_rect = std::mem::replace(&mut self.rect, new_rect);
1081 if old_rect.size() != self.rect.size() {
1082 self.send_window_size_message();
1083 self.pinch_zoom.resize_unscaled_viewport(new_rect);
1084 self.send_pinch_zoom_infos_to_script();
1085 }
1086 old_rect != self.rect
1087 }
1088
1089 pub fn set_viewport_description(&mut self, viewport_description: ViewportDescription) {
1090 self.viewport_description = viewport_description;
1091 self.send_window_size_message();
1092 self.adjust_pinch_zoom(
1093 self.viewport_description.initial_scale.get(),
1094 DevicePoint::origin(),
1095 );
1096 }
1097
1098 pub(crate) fn scroll_trees_memory_usage(
1099 &self,
1100 ops: &mut malloc_size_of::MallocSizeOfOps,
1101 ) -> usize {
1102 self.pipelines
1103 .values()
1104 .map(|pipeline| pipeline.scroll_tree.size_of(ops))
1105 .sum::<usize>()
1106 }
1107
1108 pub(crate) fn notify_input_event_handled(
1109 &mut self,
1110 render_api: &RenderApi,
1111 repaint_reason: &Cell<RepaintReason>,
1112 id: InputEventId,
1113 result: InputEventResult,
1114 ) {
1115 if let Some(pending_touch_input_event) =
1116 self.touch_handler.take_pending_touch_input_event(id)
1117 {
1118 self.on_touch_event_processed(render_api, pending_touch_input_event, result);
1119 self.touch_handler
1120 .add_touch_move_refresh_observer_if_necessary(
1121 self.refresh_driver.clone(),
1122 repaint_reason,
1123 );
1124 }
1125
1126 if let Some(wheel_event) = self.pending_wheel_events.remove(&id) &&
1127 !result.contains(InputEventResult::DefaultPrevented)
1128 {
1129 let scroll_delta =
1131 DeviceVector2D::new(-wheel_event.delta.x as f32, -wheel_event.delta.y as f32);
1132 self.notify_scroll_event(Scroll::Delta(scroll_delta.into()), wheel_event.point);
1133 }
1134 }
1135}
1136
1137#[derive(Clone, Copy, Debug, PartialEq)]
1138pub struct UnknownWebView(pub WebViewId);