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, Size2D, 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 screen_size: Size2D<f32, DevicePixel>,
121
122 embedder_to_constellation_sender: Sender<EmbedderToConstellationMessage>,
127 refresh_driver: Rc<BaseRefreshDriver>,
129 webrender_document: DocumentId,
131}
132
133impl WebViewRenderer {
134 pub(crate) fn new(
135 renderer_webview: Box<dyn WebViewTrait>,
136 viewport_details: ViewportDetails,
137 embedder_to_constellation_sender: Sender<EmbedderToConstellationMessage>,
138 refresh_driver: Rc<BaseRefreshDriver>,
139 webrender_document: DocumentId,
140 ) -> Self {
141 let hidpi_scale_factor = viewport_details.hidpi_scale_factor;
142 let size = viewport_details.size * viewport_details.hidpi_scale_factor;
143 let rect = DeviceRect::from_origin_and_size(DevicePoint::origin(), size);
144 let webview_id = renderer_webview.id();
145 Self {
146 id: webview_id,
147 webview: renderer_webview,
148 root_pipeline_id: None,
149 rect,
150 pipelines: Default::default(),
151 touch_handler: TouchHandler::new(webview_id),
152 pending_scroll_zoom_events: Default::default(),
153 pending_wheel_events: Default::default(),
154 page_zoom: DEFAULT_PAGE_ZOOM,
155 pinch_zoom: PinchZoom::new(rect),
156 hidpi_scale_factor: Scale::new(hidpi_scale_factor.0),
157 hidden: false,
158 animating: false,
159 viewport_description: Default::default(),
160 screen_size: viewport_details.device_size,
161 embedder_to_constellation_sender,
162 refresh_driver,
163 webrender_document,
164 }
165 }
166
167 fn hit_test(&self, webrender_api: &RenderApi, point: DevicePoint) -> Vec<PaintHitTestResult> {
168 Painter::hit_test_at_point_with_api_and_document(
169 webrender_api,
170 self.webrender_document,
171 point,
172 )
173 }
174
175 pub(crate) fn animation_callbacks_running(&self) -> bool {
176 self.pipelines
177 .values()
178 .any(PipelineDetails::animation_callbacks_running)
179 }
180
181 pub(crate) fn animating(&self) -> bool {
182 self.animating
183 }
184
185 pub(crate) fn hidden(&self) -> bool {
186 self.hidden
187 }
188
189 pub(crate) fn set_hidden(&mut self, new_value: bool) -> bool {
192 let old_value = std::mem::replace(&mut self.hidden, new_value);
193 new_value != old_value
194 }
195
196 pub(crate) fn ensure_pipeline_details(
198 &mut self,
199 pipeline_id: PipelineId,
200 ) -> &mut PipelineDetails {
201 self.pipelines
202 .entry(pipeline_id)
203 .or_insert_with(PipelineDetails::new)
204 }
205
206 pub(crate) fn pipeline_exited(&mut self, pipeline_id: PipelineId, source: PipelineExitSource) {
207 let pipeline = self.pipelines.entry(pipeline_id);
208 let Entry::Occupied(mut pipeline) = pipeline else {
209 return;
210 };
211
212 pipeline.get_mut().exited.insert(source);
213
214 if !pipeline.get().exited.is_all() {
218 return;
219 }
220
221 pipeline.remove_entry();
222 }
223
224 pub(crate) fn set_frame_tree(&mut self, frame_tree: &SendableFrameTree) {
225 let pipeline_id = frame_tree.pipeline.id;
226 let old_pipeline_id = self.root_pipeline_id.replace(pipeline_id);
227
228 if old_pipeline_id != self.root_pipeline_id {
229 debug!(
230 "Updating webview ({:?}) from pipeline {:?} to {:?}",
231 3, old_pipeline_id, self.root_pipeline_id
232 );
233 }
234
235 self.set_frame_tree_on_pipeline_details(frame_tree, None);
236 }
237
238 pub(crate) fn send_scroll_positions_to_layout_for_pipeline(
239 &self,
240 pipeline_id: PipelineId,
241 scrolled_node: ExternalScrollId,
242 ) {
243 let Some(details) = self.pipelines.get(&pipeline_id) else {
244 return;
245 };
246
247 let offsets = details.scroll_tree.scroll_offsets();
248
249 if offsets.is_empty() {
254 return;
255 }
256
257 let _ = self.embedder_to_constellation_sender.send(
258 EmbedderToConstellationMessage::SetScrollStates(
259 pipeline_id,
260 ScrollStateUpdate {
261 scrolled_node,
262 offsets,
263 },
264 ),
265 );
266 }
267
268 pub(crate) fn set_frame_tree_on_pipeline_details(
269 &mut self,
270 frame_tree: &SendableFrameTree,
271 parent_pipeline_id: Option<PipelineId>,
272 ) {
273 let pipeline_id = frame_tree.pipeline.id;
274 let pipeline_details = self.ensure_pipeline_details(pipeline_id);
275 pipeline_details.pipeline = Some(frame_tree.pipeline.clone());
276 pipeline_details.parent_pipeline_id = parent_pipeline_id;
277 pipeline_details.children = frame_tree
278 .children
279 .iter()
280 .map(|frame_tree| frame_tree.pipeline.id)
281 .collect();
282
283 for kid in &frame_tree.children {
284 self.set_frame_tree_on_pipeline_details(kid, Some(pipeline_id));
285 }
286 }
287
288 pub(crate) fn change_pipeline_running_animations_state(
291 &mut self,
292 pipeline_id: PipelineId,
293 animation_state: AnimationState,
294 ) -> bool {
295 let pipeline_details = self.ensure_pipeline_details(pipeline_id);
296 let was_animating = pipeline_details.animating();
297 match animation_state {
298 AnimationState::AnimationsPresent => {
299 pipeline_details.animations_running = true;
300 },
301 AnimationState::AnimationCallbacksPresent => {
302 pipeline_details.animation_callbacks_running = true;
303 },
304 AnimationState::NoAnimationsPresent => {
305 pipeline_details.animations_running = false;
306 },
307 AnimationState::NoAnimationCallbacksPresent => {
308 pipeline_details.animation_callbacks_running = false;
309 },
310 }
311 let started_animating = !was_animating && pipeline_details.animating();
312
313 self.update_animation_state();
314
315 started_animating
320 }
321
322 pub(crate) fn set_throttled(&mut self, pipeline_id: PipelineId, throttled: bool) -> bool {
325 let pipeline_details = self.ensure_pipeline_details(pipeline_id);
326 let was_animating = pipeline_details.animating();
327 pipeline_details.throttled = throttled;
328 let started_animating = !was_animating && pipeline_details.animating();
329
330 self.update_animation_state();
332
333 started_animating
338 }
339
340 fn update_animation_state(&mut self) {
341 self.animating = self.pipelines.values().any(PipelineDetails::animating);
342 self.webview.set_animating(self.animating());
343 }
344
345 pub(crate) fn for_each_connected_pipeline(&self, callback: &mut impl FnMut(&PipelineDetails)) {
346 if let Some(root_pipeline_id) = self.root_pipeline_id {
347 self.for_each_connected_pipeline_internal(root_pipeline_id, callback);
348 }
349 }
350
351 fn for_each_connected_pipeline_internal(
352 &self,
353 pipeline_id: PipelineId,
354 callback: &mut impl FnMut(&PipelineDetails),
355 ) {
356 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
357 return;
358 };
359 callback(pipeline);
360 for child_pipeline_id in &pipeline.children {
361 self.for_each_connected_pipeline_internal(*child_pipeline_id, callback);
362 }
363 }
364
365 pub(crate) fn update_touch_handling_at_new_frame_start(&mut self) -> bool {
369 let Some(fling_action) = self.touch_handler.notify_new_frame_start() else {
370 return false;
371 };
372
373 self.on_scroll_window_event(
374 Scroll::Delta((-fling_action.delta).into()),
375 fling_action.cursor,
376 );
377 true
378 }
379
380 fn dispatch_input_event_with_hit_testing(
381 &mut self,
382 render_api: &RenderApi,
383 event: InputEventAndId,
384 ) -> bool {
385 let event_point = event
386 .event
387 .point()
388 .map(|point| point.as_device_point(self.device_pixels_per_page_pixel()));
389 let hit_test_result = match event_point {
390 Some(point) => {
391 let hit_test_result = match event.event {
392 InputEvent::Touch(_) => self.touch_handler.get_hit_test_result_cache_value(),
393 _ => None,
394 }
395 .or_else(|| self.hit_test(render_api, point).into_iter().nth(0));
396 if hit_test_result.is_none() {
397 warn!("Empty hit test result for input event, ignoring.");
398 return false;
399 }
400 hit_test_result
401 },
402 None => None,
403 };
404
405 if let Err(error) = self.embedder_to_constellation_sender.send(
406 EmbedderToConstellationMessage::ForwardInputEvent(self.id, event, hit_test_result),
407 ) {
408 warn!("Sending event to constellation failed ({error:?}).");
409 false
410 } else {
411 true
412 }
413 }
414
415 pub(crate) fn notify_input_event(
416 &mut self,
417 render_api: &RenderApi,
418 repaint_reason: &Cell<RepaintReason>,
419 event_and_id: InputEventAndId,
420 ) -> bool {
421 if let InputEvent::Touch(touch_event) = event_and_id.event {
422 return self.on_touch_event(render_api, repaint_reason, touch_event, event_and_id.id);
423 }
424
425 if let InputEvent::Wheel(wheel_event) = event_and_id.event {
426 self.pending_wheel_events
427 .insert(event_and_id.id, wheel_event);
428 }
429
430 self.dispatch_input_event_with_hit_testing(render_api, event_and_id)
431 }
432
433 fn send_touch_event(
434 &mut self,
435 render_api: &RenderApi,
436 event: TouchEvent,
437 id: InputEventId,
438 ) -> bool {
439 let cancelable = event.is_cancelable();
440 let event_type = event.event_type;
441
442 let input_event_and_id = InputEventAndId {
443 event: InputEvent::Touch(event),
444 id,
445 };
446
447 let result = self.dispatch_input_event_with_hit_testing(render_api, input_event_and_id);
448
449 if cancelable && result {
453 self.touch_handler
454 .add_pending_touch_input_event(id, event.touch_id, event_type);
455 }
456
457 result
458 }
459
460 pub(crate) fn on_touch_event(
461 &mut self,
462 render_api: &RenderApi,
463 repaint_reason: &Cell<RepaintReason>,
464 event: TouchEvent,
465 id: InputEventId,
466 ) -> bool {
467 let result = match event.event_type {
468 TouchEventType::Down => self.on_touch_down(render_api, event, id),
469 TouchEventType::Move => self.on_touch_move(render_api, event, id),
470 TouchEventType::Up => self.on_touch_up(render_api, event, id),
471 TouchEventType::Cancel => self.on_touch_cancel(render_api, event, id),
472 };
473
474 self.touch_handler
475 .add_touch_move_refresh_observer_if_necessary(
476 self.refresh_driver.clone(),
477 repaint_reason,
478 );
479 result
480 }
481
482 fn on_touch_down(
483 &mut self,
484 render_api: &RenderApi,
485 event: TouchEvent,
486 id: InputEventId,
487 ) -> bool {
488 let point = event
489 .point
490 .as_device_point(self.device_pixels_per_page_pixel());
491 self.touch_handler.on_touch_down(event.touch_id, point);
492 self.send_touch_event(render_api, event, id)
493 }
494
495 fn on_touch_move(
496 &mut self,
497 render_api: &RenderApi,
498 mut event: TouchEvent,
499 id: InputEventId,
500 ) -> bool {
501 let point = event
502 .point
503 .as_device_point(self.device_pixels_per_page_pixel());
504 let action = self.touch_handler.on_touch_move(
505 event.touch_id,
506 point,
507 self.device_pixels_per_page_pixel_not_including_pinch_zoom()
508 .get(),
509 );
510 if let Some(action) = action {
511 if self
514 .touch_handler
515 .move_allowed(self.touch_handler.current_sequence_id)
516 {
517 event.disable_cancelable();
519 self.pending_scroll_zoom_events.push(action);
520 }
521 }
522 let mut reached_constellation = false;
523 if !self.touch_handler.is_handling_touch_move_for_touch_id(
527 self.touch_handler.current_sequence_id,
528 event.touch_id,
529 ) {
530 reached_constellation = self.send_touch_event(render_api, event, id);
531 if reached_constellation && event.is_cancelable() {
532 self.touch_handler.set_handling_touch_move_for_touch_id(
533 self.touch_handler.current_sequence_id,
534 event.touch_id,
535 TouchIdMoveTracking::Track,
536 );
537 }
538 }
539 reached_constellation
540 }
541
542 fn on_touch_up(&mut self, render_api: &RenderApi, event: TouchEvent, id: InputEventId) -> bool {
543 let point = event
544 .point
545 .as_device_point(self.device_pixels_per_page_pixel());
546 self.touch_handler.on_touch_up(event.touch_id, point);
547 self.send_touch_event(render_api, event, id)
548 }
549
550 fn on_touch_cancel(
551 &mut self,
552 render_api: &RenderApi,
553 event: TouchEvent,
554 id: InputEventId,
555 ) -> bool {
556 let point = event
557 .point
558 .as_device_point(self.device_pixels_per_page_pixel());
559 self.touch_handler.on_touch_cancel(event.touch_id, point);
560 self.send_touch_event(render_api, event, id)
561 }
562
563 fn on_touch_event_processed(
564 &mut self,
565 render_api: &RenderApi,
566 pending_touch_input_event: PendingTouchInputEvent,
567 result: InputEventResult,
568 ) {
569 let PendingTouchInputEvent {
570 sequence_id,
571 event_type,
572 touch_id,
573 } = pending_touch_input_event;
574
575 if result.contains(InputEventResult::DefaultPrevented) {
576 debug!(
577 "Touch event {:?} in sequence {:?} prevented!",
578 event_type, sequence_id
579 );
580 match event_type {
581 TouchEventType::Down => {
582 self.touch_handler.prevent_click(sequence_id);
584 self.touch_handler.prevent_move(sequence_id);
585 self.touch_handler
586 .remove_pending_touch_move_actions(sequence_id);
587 },
588 TouchEventType::Move => {
589 if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) {
591 info.prevent_move = TouchMoveAllowed::Prevented;
592 if let TouchSequenceState::PendingFling { .. } = info.state {
593 info.state = TouchSequenceState::Finished;
594 }
595 self.touch_handler.set_handling_touch_move_for_touch_id(
596 self.touch_handler.current_sequence_id,
597 touch_id,
598 TouchIdMoveTracking::Remove,
599 );
600 self.touch_handler
601 .remove_pending_touch_move_actions(sequence_id);
602 }
603 },
604 TouchEventType::Up => {
605 let Some(info) = &mut self.touch_handler.get_touch_sequence_mut(sequence_id)
609 else {
610 return;
615 };
616 match info.state {
617 TouchSequenceState::PendingClick(_) => {
618 info.state = TouchSequenceState::Finished;
619 self.touch_handler.remove_touch_sequence(sequence_id);
620 },
621 TouchSequenceState::Flinging { .. } => {
622 },
624 TouchSequenceState::Finished => {
625 self.touch_handler.remove_touch_sequence(sequence_id);
626 },
627 TouchSequenceState::Touching |
628 TouchSequenceState::Panning { .. } |
629 TouchSequenceState::Pinching |
630 TouchSequenceState::MultiTouch |
631 TouchSequenceState::PendingFling { .. } => {
632 },
637 }
638 },
639 TouchEventType::Cancel => {
640 self.touch_handler
643 .remove_pending_touch_move_actions(sequence_id);
644 self.touch_handler.try_remove_touch_sequence(sequence_id);
645 },
646 }
647 } else {
648 debug!(
649 "Touch event {:?} in sequence {:?} allowed",
650 event_type, sequence_id
651 );
652 match event_type {
653 TouchEventType::Down => {},
654 TouchEventType::Move => {
655 self.pending_scroll_zoom_events.extend(
656 self.touch_handler
657 .take_pending_touch_move_actions(sequence_id),
658 );
659 self.touch_handler.set_handling_touch_move_for_touch_id(
660 self.touch_handler.current_sequence_id,
661 touch_id,
662 TouchIdMoveTracking::Remove,
663 );
664 if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) &&
665 info.prevent_move == TouchMoveAllowed::Pending
666 {
667 info.prevent_move = TouchMoveAllowed::Allowed;
668 if let TouchSequenceState::PendingFling { velocity, point } = info.state {
669 info.state = TouchSequenceState::Flinging { velocity, point }
670 }
671 }
672 },
673 TouchEventType::Up => {
674 let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) else {
675 return;
677 };
678 match info.state {
679 TouchSequenceState::PendingClick(point) => {
680 info.state = TouchSequenceState::Finished;
681 if !info.prevent_click {
684 self.simulate_mouse_click(render_api, point);
685 }
686 self.touch_handler.remove_touch_sequence(sequence_id);
687 },
688 TouchSequenceState::Flinging { .. } => {
689 },
691 TouchSequenceState::Finished => {
692 self.touch_handler.remove_touch_sequence(sequence_id);
693 },
694 TouchSequenceState::Panning { .. } |
695 TouchSequenceState::Pinching |
696 TouchSequenceState::PendingFling { .. } => {
697 },
702 TouchSequenceState::MultiTouch | TouchSequenceState::Touching => {
703 },
705 }
706 },
707 TouchEventType::Cancel => {
708 self.touch_handler
709 .remove_pending_touch_move_actions(sequence_id);
710 self.touch_handler.try_remove_touch_sequence(sequence_id);
711 },
712 }
713 }
714 }
715
716 fn simulate_mouse_click(&mut self, render_api: &RenderApi, point: DevicePoint) {
718 let button = MouseButton::Left;
719 self.dispatch_input_event_with_hit_testing(
720 render_api,
721 InputEvent::MouseMove(MouseMoveEvent::new_compatibility_for_touch(point.into())).into(),
722 );
723 self.dispatch_input_event_with_hit_testing(
724 render_api,
725 InputEvent::MouseButton(MouseButtonEvent::new(
726 MouseButtonAction::Down,
727 button,
728 point.into(),
729 ))
730 .into(),
731 );
732 self.dispatch_input_event_with_hit_testing(
733 render_api,
734 InputEvent::MouseButton(MouseButtonEvent::new(
735 MouseButtonAction::Up,
736 button,
737 point.into(),
738 ))
739 .into(),
740 );
741 }
742
743 pub(crate) fn notify_scroll_event(&mut self, scroll: Scroll, point: WebViewPoint) {
744 let point = point.as_device_point(self.device_pixels_per_page_pixel());
745 self.on_scroll_window_event(scroll, point);
746 }
747
748 fn on_scroll_window_event(&mut self, scroll: Scroll, cursor: DevicePoint) {
749 self.pending_scroll_zoom_events
750 .push(ScrollZoomEvent::Scroll(ScrollEvent {
751 scroll,
752 point: cursor,
753 }));
754 }
755
756 pub(crate) fn process_pending_scroll_and_pinch_zoom_events(
763 &mut self,
764 render_api: &RenderApi,
765 ) -> (PinchZoomResult, Option<ScrollResult>) {
766 if self.pending_scroll_zoom_events.is_empty() {
767 return (PinchZoomResult::DidNotPinchZoom, None);
768 }
769
770 let mut combined_scroll_event: Option<ScrollEvent> = None;
773 let mut new_pinch_zoom = self.pinch_zoom;
774 let device_pixels_per_page_pixel = self.device_pixels_per_page_pixel();
775
776 for scroll_event in self.pending_scroll_zoom_events.drain(..) {
777 match scroll_event {
778 ScrollZoomEvent::PinchZoom(magnification, center) => {
779 let new_factor = self
780 .viewport_description
781 .clamp_zoom(self.pinch_zoom.zoom_factor().0 * magnification);
782 new_pinch_zoom.set_zoom(new_factor, center);
783 },
784 ScrollZoomEvent::Scroll(scroll_event_info) => {
785 let combined_event = match combined_scroll_event.as_mut() {
786 None => {
787 combined_scroll_event = Some(scroll_event_info);
788 continue;
789 },
790 Some(combined_event) => combined_event,
791 };
792
793 match (combined_event.scroll, scroll_event_info.scroll) {
794 (Scroll::Delta(old_delta), Scroll::Delta(new_delta)) => {
795 let old_delta =
796 old_delta.as_device_vector(device_pixels_per_page_pixel);
797 let new_delta =
798 new_delta.as_device_vector(device_pixels_per_page_pixel);
799 combined_event.scroll = Scroll::Delta((old_delta + new_delta).into());
800 },
801 (Scroll::Start, _) | (Scroll::End, _) => {
802 break;
804 },
805 (_, Scroll::Start) | (_, Scroll::End) => {
806 *combined_event = scroll_event_info;
809 break;
810 },
811 }
812 },
813 }
814 }
815
816 if let Some(combined_scroll_event) = combined_scroll_event.as_mut() {
820 new_pinch_zoom.pan(
821 &mut combined_scroll_event.scroll,
822 self.device_pixels_per_page_pixel(),
823 )
824 }
825
826 let scroll_result = combined_scroll_event.and_then(|combined_event| {
827 self.scroll_node_at_device_point(
828 render_api,
829 combined_event.point.to_f32(),
830 combined_event.scroll,
831 )
832 });
833 if let Some(ref scroll_result) = scroll_result {
834 self.send_scroll_positions_to_layout_for_pipeline(
835 scroll_result.hit_test_result.pipeline_id,
836 scroll_result.external_scroll_id,
837 );
838 } else {
839 self.touch_handler.stop_fling_if_needed();
840 }
841
842 let pinch_zoom_result = self.set_pinch_zoom(new_pinch_zoom);
844 if pinch_zoom_result == PinchZoomResult::DidPinchZoom {
845 self.send_pinch_zoom_infos_to_script();
846 }
847
848 (pinch_zoom_result, scroll_result)
849 }
850
851 fn scroll_node_at_device_point(
856 &mut self,
857 render_api: &RenderApi,
858 cursor: DevicePoint,
859 scroll: Scroll,
860 ) -> Option<ScrollResult> {
861 let scroll_location = match scroll {
862 Scroll::Delta(delta) => {
863 let device_pixels_per_page = self.device_pixels_per_page_pixel();
864 let calculate_delta =
865 delta.as_device_vector(device_pixels_per_page) / device_pixels_per_page;
866 ScrollLocation::Delta(calculate_delta.cast_unit())
867 },
868 Scroll::Start => ScrollLocation::Start,
869 Scroll::End => ScrollLocation::End,
870 };
871
872 let hit_test_results: Vec<_> = self
873 .touch_handler
874 .get_hit_test_result_cache_value()
875 .map(|result| vec![result])
876 .unwrap_or_else(|| self.hit_test(render_api, cursor));
877
878 let mut previous_pipeline_id = None;
882 for hit_test_result in hit_test_results {
883 let pipeline_details = self.pipelines.get_mut(&hit_test_result.pipeline_id)?;
884 if previous_pipeline_id.replace(hit_test_result.pipeline_id) !=
885 Some(hit_test_result.pipeline_id)
886 {
887 let scroll_result = pipeline_details.scroll_tree.scroll_node_or_ancestor(
888 hit_test_result.external_scroll_id,
889 scroll_location,
890 ScrollType::InputEvents,
891 );
892 if let Some((external_scroll_id, offset)) = scroll_result {
893 self.touch_handler.set_hit_test_result_cache_value(
899 hit_test_result.clone(),
900 self.device_pixels_per_page_pixel(),
901 );
902 return Some(ScrollResult {
903 hit_test_result,
904 external_scroll_id,
905 offset,
906 });
907 }
908 }
909 }
910 None
911 }
912
913 pub(crate) fn scroll_viewport_by_delta(
917 &mut self,
918 delta: LayoutVector2D,
919 ) -> (PinchZoomResult, Vec<ScrollResult>) {
920 let device_pixels_per_page_pixel = self.device_pixels_per_page_pixel();
921 let delta_in_device_pixels = delta.cast_unit() * device_pixels_per_page_pixel;
922 let remaining = self.pinch_zoom.pan_with_device_scroll(
923 Scroll::Delta(delta_in_device_pixels.into()),
924 device_pixels_per_page_pixel,
925 );
926
927 let pinch_zoom_result = match remaining == delta_in_device_pixels {
928 true => PinchZoomResult::DidNotPinchZoom,
929 false => PinchZoomResult::DidPinchZoom,
930 };
931 if remaining == Vector2D::zero() {
932 return (pinch_zoom_result, vec![]);
933 }
934
935 let Some(root_pipeline_id) = self.root_pipeline_id else {
936 return (pinch_zoom_result, vec![]);
937 };
938 let Some(root_pipeline) = self.pipelines.get_mut(&root_pipeline_id) else {
939 return (pinch_zoom_result, vec![]);
940 };
941
942 let remaining = remaining / device_pixels_per_page_pixel;
943 let Some((external_scroll_id, offset)) = root_pipeline.scroll_tree.scroll_node_or_ancestor(
944 ExternalScrollId(0, root_pipeline_id.into()),
945 ScrollLocation::Delta(remaining.cast_unit()),
946 ScrollType::InputEvents,
948 ) else {
949 return (pinch_zoom_result, vec![]);
950 };
951
952 let hit_test_result = PaintHitTestResult {
953 pipeline_id: root_pipeline_id,
954 point_in_viewport: Default::default(),
957 external_scroll_id,
958 };
959
960 self.send_scroll_positions_to_layout_for_pipeline(root_pipeline_id, external_scroll_id);
961
962 if pinch_zoom_result == PinchZoomResult::DidPinchZoom {
963 self.send_pinch_zoom_infos_to_script();
964 }
965
966 let scroll_result = ScrollResult {
967 hit_test_result,
968 external_scroll_id,
969 offset,
970 };
971 (pinch_zoom_result, vec![scroll_result])
972 }
973
974 fn send_pinch_zoom_infos_to_script(&self) {
976 let Some(pipeline_id) = self.root_pipeline_id else {
978 return;
979 };
980
981 let pinch_zoom_infos = self.pinch_zoom.get_pinch_zoom_infos_for_script(
982 self.device_pixels_per_page_pixel_not_including_pinch_zoom(),
983 );
984
985 let _ = self.embedder_to_constellation_sender.send(
986 EmbedderToConstellationMessage::UpdatePinchZoomInfos(pipeline_id, pinch_zoom_infos),
987 );
988 }
989
990 pub(crate) fn pinch_zoom(&self) -> PinchZoom {
991 self.pinch_zoom
992 }
993
994 fn set_pinch_zoom(&mut self, requested_pinch_zoom: PinchZoom) -> PinchZoomResult {
995 if requested_pinch_zoom == self.pinch_zoom {
996 return PinchZoomResult::DidNotPinchZoom;
997 }
998
999 self.pinch_zoom = requested_pinch_zoom;
1000 PinchZoomResult::DidPinchZoom
1001 }
1002
1003 pub(crate) fn set_page_zoom(
1004 &mut self,
1005 new_page_zoom: Scale<f32, CSSPixel, DeviceIndependentPixel>,
1006 ) {
1007 let new_page_zoom = new_page_zoom.clamp(MIN_PAGE_ZOOM, MAX_PAGE_ZOOM);
1008 let old_zoom = std::mem::replace(&mut self.page_zoom, new_page_zoom);
1009 if old_zoom != self.page_zoom {
1010 self.send_window_size_message();
1011 }
1012 }
1013
1014 pub(crate) fn device_pixels_per_page_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> {
1020 let viewport_scale = self
1021 .root_pipeline_id
1022 .and_then(|pipeline_id| self.pipelines.get(&pipeline_id))
1023 .and_then(|pipeline| pipeline.viewport_scale)
1024 .unwrap_or_else(|| self.page_zoom * self.hidpi_scale_factor);
1025 viewport_scale * self.pinch_zoom.zoom_factor()
1026 }
1027
1028 pub(crate) fn device_pixels_per_page_pixel_not_including_pinch_zoom(
1033 &self,
1034 ) -> Scale<f32, CSSPixel, DevicePixel> {
1035 self.page_zoom * self.hidpi_scale_factor
1036 }
1037
1038 pub(crate) fn adjust_pinch_zoom(&mut self, magnification: f32, center: DevicePoint) {
1040 if magnification == 1.0 {
1041 return;
1042 }
1043
1044 self.pending_scroll_zoom_events
1045 .push(ScrollZoomEvent::PinchZoom(magnification, center));
1046 }
1047
1048 fn send_window_size_message(&self) {
1049 let device_pixel_ratio = self.device_pixels_per_page_pixel_not_including_pinch_zoom();
1052 let layout_viewport = self.rect.size().to_f32() /
1055 (device_pixel_ratio * Scale::new(self.viewport_description.initial_scale.get()));
1056 let _ = self.embedder_to_constellation_sender.send(
1057 EmbedderToConstellationMessage::ChangeViewportDetails(
1058 self.id,
1059 ViewportDetails {
1060 hidpi_scale_factor: device_pixel_ratio,
1061 size: layout_viewport,
1062 device_size: self.screen_size,
1063 },
1064 WindowSizeType::Resize,
1065 ),
1066 );
1067 }
1068
1069 pub(crate) fn set_hidpi_scale_factor(
1071 &mut self,
1072 new_scale: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1073 ) -> bool {
1074 let old_scale_factor = std::mem::replace(&mut self.hidpi_scale_factor, new_scale);
1075 if self.hidpi_scale_factor == old_scale_factor {
1076 return false;
1077 }
1078
1079 self.send_window_size_message();
1080 true
1081 }
1082
1083 pub(crate) fn set_screen_size(&mut self, new_size: Size2D<f32, DevicePixel>) -> bool {
1085 if self.screen_size == new_size {
1086 return false;
1087 }
1088 self.screen_size = new_size;
1089
1090 self.send_window_size_message();
1091 true
1092 }
1093
1094 pub(crate) fn set_rect(&mut self, new_rect: DeviceRect) -> bool {
1096 let old_rect = std::mem::replace(&mut self.rect, new_rect);
1097 if old_rect.size() != self.rect.size() {
1098 self.send_window_size_message();
1099 self.pinch_zoom.resize_unscaled_viewport(new_rect);
1100 self.send_pinch_zoom_infos_to_script();
1101 }
1102 old_rect != self.rect
1103 }
1104
1105 pub fn set_viewport_description(&mut self, viewport_description: ViewportDescription) {
1106 self.viewport_description = viewport_description;
1107 self.send_window_size_message();
1108 self.adjust_pinch_zoom(
1109 self.viewport_description.initial_scale.get(),
1110 DevicePoint::origin(),
1111 );
1112 }
1113
1114 pub(crate) fn scroll_trees_memory_usage(
1115 &self,
1116 ops: &mut malloc_size_of::MallocSizeOfOps,
1117 ) -> usize {
1118 self.pipelines
1119 .values()
1120 .map(|pipeline| pipeline.scroll_tree.size_of(ops))
1121 .sum::<usize>()
1122 }
1123
1124 pub(crate) fn notify_input_event_handled(
1125 &mut self,
1126 render_api: &RenderApi,
1127 repaint_reason: &Cell<RepaintReason>,
1128 id: InputEventId,
1129 result: InputEventResult,
1130 ) {
1131 if let Some(pending_touch_input_event) =
1132 self.touch_handler.take_pending_touch_input_event(id)
1133 {
1134 self.on_touch_event_processed(render_api, pending_touch_input_event, result);
1135 self.touch_handler
1136 .add_touch_move_refresh_observer_if_necessary(
1137 self.refresh_driver.clone(),
1138 repaint_reason,
1139 );
1140 }
1141
1142 if let Some(wheel_event) = self.pending_wheel_events.remove(&id) &&
1143 !result.contains(InputEventResult::DefaultPrevented)
1144 {
1145 let scroll_delta =
1147 DeviceVector2D::new(-wheel_event.delta.x as f32, -wheel_event.delta.y as f32);
1148 self.notify_scroll_event(Scroll::Delta(scroll_delta.into()), wheel_event.point);
1149 }
1150 }
1151}
1152
1153#[derive(Clone, Copy, Debug, PartialEq)]
1154pub struct UnknownWebView(pub WebViewId);