1use std::cell::Cell;
6use std::collections::hash_map::Entry;
7use std::rc::Rc;
8
9use base::id::{PipelineId, WebViewId};
10use constellation_traits::{EmbedderToConstellationMessage, ScrollStateUpdate, WindowSizeType};
11use crossbeam_channel::Sender;
12use embedder_traits::{
13 AnimationState, InputEvent, InputEventAndId, InputEventId, InputEventResult, MouseButton,
14 MouseButtonAction, MouseButtonEvent, MouseMoveEvent, PaintHitTestResult, Scroll, TouchEvent,
15 TouchEventType, ViewportDetails, WebViewPoint, WheelEvent,
16};
17use euclid::{Scale, Vector2D};
18use log::{debug, warn};
19use malloc_size_of::MallocSizeOf;
20use paint_api::display_list::ScrollType;
21use paint_api::viewport_description::{
22 DEFAULT_PAGE_ZOOM, MAX_PAGE_ZOOM, MIN_PAGE_ZOOM, ViewportDescription,
23};
24use paint_api::{PipelineExitSource, SendableFrameTree, WebViewTrait};
25use rustc_hash::FxHashMap;
26use servo_geometry::DeviceIndependentPixel;
27use style_traits::CSSPixel;
28use webrender::RenderApi;
29use webrender_api::units::{DevicePixel, DevicePoint, DeviceRect, DeviceVector2D, LayoutVector2D};
30use webrender_api::{DocumentId, ExternalScrollId, ScrollLocation};
31
32use crate::paint::RepaintReason;
33use crate::painter::Painter;
34use crate::pinch_zoom::PinchZoom;
35use crate::pipeline_details::PipelineDetails;
36use crate::refresh_driver::BaseRefreshDriver;
37use crate::touch::{
38 PendingTouchInputEvent, TouchHandler, TouchIdMoveTracking, TouchMoveAllowed, TouchSequenceState,
39};
40
41#[derive(Clone, Copy)]
42pub(crate) struct ScrollEvent {
43 pub scroll: Scroll,
45 pub point: DevicePoint,
47 pub event_count: u32,
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 ) {
417 if let InputEvent::Touch(touch_event) = event_and_id.event {
418 self.on_touch_event(render_api, repaint_reason, touch_event, event_and_id.id);
419 return;
420 }
421
422 if let InputEvent::Wheel(wheel_event) = event_and_id.event {
423 self.pending_wheel_events
424 .insert(event_and_id.id, wheel_event);
425 }
426
427 self.dispatch_input_event_with_hit_testing(render_api, event_and_id);
428 }
429
430 fn send_touch_event(
431 &mut self,
432 render_api: &RenderApi,
433 event: TouchEvent,
434 id: InputEventId,
435 ) -> bool {
436 let cancelable = event.is_cancelable();
437 let event_type = event.event_type;
438
439 let input_event_and_id = InputEventAndId {
440 event: InputEvent::Touch(event),
441 id,
442 };
443
444 let result = self.dispatch_input_event_with_hit_testing(render_api, input_event_and_id);
445
446 if cancelable && result {
450 self.touch_handler
451 .add_pending_touch_input_event(id, event.touch_id, event_type);
452 }
453
454 result
455 }
456
457 pub(crate) fn on_touch_event(
458 &mut self,
459 render_api: &RenderApi,
460 repaint_reason: &Cell<RepaintReason>,
461 event: TouchEvent,
462 id: InputEventId,
463 ) {
464 match event.event_type {
465 TouchEventType::Down => self.on_touch_down(render_api, event, id),
466 TouchEventType::Move => self.on_touch_move(render_api, event, id),
467 TouchEventType::Up => self.on_touch_up(render_api, event, id),
468 TouchEventType::Cancel => self.on_touch_cancel(render_api, event, id),
469 }
470
471 self.touch_handler
472 .add_touch_move_refresh_observer_if_necessary(
473 self.refresh_driver.clone(),
474 repaint_reason,
475 );
476 }
477
478 fn on_touch_down(&mut self, render_api: &RenderApi, event: TouchEvent, id: InputEventId) {
479 let point = event
480 .point
481 .as_device_point(self.device_pixels_per_page_pixel());
482 self.touch_handler.on_touch_down(event.touch_id, point);
483 self.send_touch_event(render_api, event, id);
484 }
485
486 fn on_touch_move(&mut self, render_api: &RenderApi, mut event: TouchEvent, id: InputEventId) {
487 let point = event
488 .point
489 .as_device_point(self.device_pixels_per_page_pixel());
490 let action = self.touch_handler.on_touch_move(
491 event.touch_id,
492 point,
493 self.device_pixels_per_page_pixel_not_including_pinch_zoom()
494 .get(),
495 );
496 if let Some(action) = action {
497 if self
500 .touch_handler
501 .move_allowed(self.touch_handler.current_sequence_id)
502 {
503 event.disable_cancelable();
505 self.pending_scroll_zoom_events.push(action);
506 }
507 }
508 if !self.touch_handler.is_handling_touch_move_for_touch_id(
512 self.touch_handler.current_sequence_id,
513 event.touch_id,
514 ) && self.send_touch_event(render_api, event, id) &&
515 event.is_cancelable()
516 {
517 self.touch_handler.set_handling_touch_move_for_touch_id(
518 self.touch_handler.current_sequence_id,
519 event.touch_id,
520 TouchIdMoveTracking::Track,
521 );
522 }
523 }
524
525 fn on_touch_up(&mut self, render_api: &RenderApi, event: TouchEvent, id: InputEventId) {
526 let point = event
527 .point
528 .as_device_point(self.device_pixels_per_page_pixel());
529 self.touch_handler.on_touch_up(event.touch_id, point);
530 self.send_touch_event(render_api, event, id);
531 }
532
533 fn on_touch_cancel(&mut self, render_api: &RenderApi, event: TouchEvent, id: InputEventId) {
534 let point = event
535 .point
536 .as_device_point(self.device_pixels_per_page_pixel());
537 self.touch_handler.on_touch_cancel(event.touch_id, point);
538 self.send_touch_event(render_api, event, id);
539 }
540
541 fn on_touch_event_processed(
542 &mut self,
543 render_api: &RenderApi,
544 pending_touch_input_event: PendingTouchInputEvent,
545 result: InputEventResult,
546 ) {
547 let PendingTouchInputEvent {
548 sequence_id,
549 event_type,
550 touch_id,
551 } = pending_touch_input_event;
552
553 if result.contains(InputEventResult::DefaultPrevented) {
554 debug!(
555 "Touch event {:?} in sequence {:?} prevented!",
556 event_type, sequence_id
557 );
558 match event_type {
559 TouchEventType::Down => {
560 self.touch_handler.prevent_click(sequence_id);
562 self.touch_handler.prevent_move(sequence_id);
563 self.touch_handler
564 .remove_pending_touch_move_actions(sequence_id);
565 },
566 TouchEventType::Move => {
567 if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) {
569 info.prevent_move = TouchMoveAllowed::Prevented;
570 if let TouchSequenceState::PendingFling { .. } = info.state {
571 info.state = TouchSequenceState::Finished;
572 }
573 self.touch_handler.set_handling_touch_move_for_touch_id(
574 self.touch_handler.current_sequence_id,
575 touch_id,
576 TouchIdMoveTracking::Remove,
577 );
578 self.touch_handler
579 .remove_pending_touch_move_actions(sequence_id);
580 }
581 },
582 TouchEventType::Up => {
583 let Some(info) = &mut self.touch_handler.get_touch_sequence_mut(sequence_id)
587 else {
588 return;
593 };
594 match info.state {
595 TouchSequenceState::PendingClick(_) => {
596 info.state = TouchSequenceState::Finished;
597 self.touch_handler.remove_touch_sequence(sequence_id);
598 },
599 TouchSequenceState::Flinging { .. } => {
600 },
602 TouchSequenceState::Finished => {
603 self.touch_handler.remove_touch_sequence(sequence_id);
604 },
605 TouchSequenceState::Touching |
606 TouchSequenceState::Panning { .. } |
607 TouchSequenceState::Pinching |
608 TouchSequenceState::MultiTouch |
609 TouchSequenceState::PendingFling { .. } => {
610 },
615 }
616 },
617 TouchEventType::Cancel => {
618 self.touch_handler
621 .remove_pending_touch_move_actions(sequence_id);
622 self.touch_handler.try_remove_touch_sequence(sequence_id);
623 },
624 }
625 } else {
626 debug!(
627 "Touch event {:?} in sequence {:?} allowed",
628 event_type, sequence_id
629 );
630 match event_type {
631 TouchEventType::Down => {},
632 TouchEventType::Move => {
633 self.pending_scroll_zoom_events.extend(
634 self.touch_handler
635 .take_pending_touch_move_actions(sequence_id),
636 );
637 self.touch_handler.set_handling_touch_move_for_touch_id(
638 self.touch_handler.current_sequence_id,
639 touch_id,
640 TouchIdMoveTracking::Remove,
641 );
642 if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) {
643 if info.prevent_move == TouchMoveAllowed::Pending {
644 info.prevent_move = TouchMoveAllowed::Allowed;
645 if let TouchSequenceState::PendingFling { velocity, point } = info.state
646 {
647 info.state = TouchSequenceState::Flinging { velocity, point }
648 }
649 }
650 }
651 },
652 TouchEventType::Up => {
653 let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) else {
654 return;
656 };
657 match info.state {
658 TouchSequenceState::PendingClick(point) => {
659 info.state = TouchSequenceState::Finished;
660 if !info.prevent_click {
663 self.simulate_mouse_click(render_api, point);
664 }
665 self.touch_handler.remove_touch_sequence(sequence_id);
666 },
667 TouchSequenceState::Flinging { .. } => {
668 },
670 TouchSequenceState::Finished => {
671 self.touch_handler.remove_touch_sequence(sequence_id);
672 },
673 TouchSequenceState::Panning { .. } |
674 TouchSequenceState::Pinching |
675 TouchSequenceState::PendingFling { .. } => {
676 },
681 TouchSequenceState::MultiTouch | TouchSequenceState::Touching => {
682 },
684 }
685 },
686 TouchEventType::Cancel => {
687 self.touch_handler
688 .remove_pending_touch_move_actions(sequence_id);
689 self.touch_handler.try_remove_touch_sequence(sequence_id);
690 },
691 }
692 }
693 }
694
695 fn simulate_mouse_click(&mut self, render_api: &RenderApi, point: DevicePoint) {
697 let button = MouseButton::Left;
698 self.dispatch_input_event_with_hit_testing(
699 render_api,
700 InputEvent::MouseMove(MouseMoveEvent::new_compatibility_for_touch(point.into())).into(),
701 );
702 self.dispatch_input_event_with_hit_testing(
703 render_api,
704 InputEvent::MouseButton(MouseButtonEvent::new(
705 MouseButtonAction::Down,
706 button,
707 point.into(),
708 ))
709 .into(),
710 );
711 self.dispatch_input_event_with_hit_testing(
712 render_api,
713 InputEvent::MouseButton(MouseButtonEvent::new(
714 MouseButtonAction::Up,
715 button,
716 point.into(),
717 ))
718 .into(),
719 );
720 }
721
722 pub(crate) fn notify_scroll_event(&mut self, scroll: Scroll, point: WebViewPoint) {
723 let point = point.as_device_point(self.device_pixels_per_page_pixel());
724 self.on_scroll_window_event(scroll, point);
725 }
726
727 fn on_scroll_window_event(&mut self, scroll: Scroll, cursor: DevicePoint) {
728 self.pending_scroll_zoom_events
729 .push(ScrollZoomEvent::Scroll(ScrollEvent {
730 scroll,
731 point: cursor,
732 event_count: 1,
733 }));
734 }
735
736 pub(crate) fn process_pending_scroll_and_pinch_zoom_events(
743 &mut self,
744 render_api: &RenderApi,
745 ) -> (PinchZoomResult, Option<ScrollResult>) {
746 if self.pending_scroll_zoom_events.is_empty() {
747 return (PinchZoomResult::DidNotPinchZoom, None);
748 }
749
750 let mut combined_scroll_event: Option<ScrollEvent> = None;
753 let mut new_pinch_zoom = self.pinch_zoom;
754 let device_pixels_per_page_pixel = self.device_pixels_per_page_pixel();
755
756 for scroll_event in self.pending_scroll_zoom_events.drain(..) {
757 match scroll_event {
758 ScrollZoomEvent::PinchZoom(factor, center) => {
759 new_pinch_zoom.zoom(factor, center);
760 },
761 ScrollZoomEvent::Scroll(scroll_event_info) => {
762 let combined_event = match combined_scroll_event.as_mut() {
763 None => {
764 combined_scroll_event = Some(scroll_event_info);
765 continue;
766 },
767 Some(combined_event) => combined_event,
768 };
769
770 match (combined_event.scroll, scroll_event_info.scroll) {
771 (Scroll::Delta(old_delta), Scroll::Delta(new_delta)) => {
772 let old_event_count = combined_event.event_count as f32;
777 combined_event.event_count += 1;
778 let new_event_count = combined_event.event_count as f32;
779 let old_delta =
780 old_delta.as_device_vector(device_pixels_per_page_pixel);
781 let new_delta =
782 new_delta.as_device_vector(device_pixels_per_page_pixel);
783 let delta = (old_delta * old_event_count + new_delta) / new_event_count;
784 combined_event.scroll = Scroll::Delta(delta.into());
785 },
786 (Scroll::Start, _) | (Scroll::End, _) => {
787 break;
789 },
790 (_, Scroll::Start) | (_, Scroll::End) => {
791 *combined_event = scroll_event_info;
794 break;
795 },
796 }
797 },
798 }
799 }
800
801 if let Some(combined_scroll_event) = combined_scroll_event.as_mut() {
805 new_pinch_zoom.pan(
806 &mut combined_scroll_event.scroll,
807 self.device_pixels_per_page_pixel(),
808 )
809 }
810
811 let scroll_result = combined_scroll_event.and_then(|combined_event| {
812 self.scroll_node_at_device_point(
813 render_api,
814 combined_event.point.to_f32(),
815 combined_event.scroll,
816 )
817 });
818 if let Some(ref scroll_result) = scroll_result {
819 self.send_scroll_positions_to_layout_for_pipeline(
820 scroll_result.hit_test_result.pipeline_id,
821 scroll_result.external_scroll_id,
822 );
823 } else {
824 self.touch_handler.stop_fling_if_needed();
825 }
826
827 let pinch_zoom_result = self.set_pinch_zoom(new_pinch_zoom);
829 if pinch_zoom_result == PinchZoomResult::DidPinchZoom {
830 self.send_pinch_zoom_infos_to_script();
831 }
832
833 (pinch_zoom_result, scroll_result)
834 }
835
836 fn scroll_node_at_device_point(
841 &mut self,
842 render_api: &RenderApi,
843 cursor: DevicePoint,
844 scroll: Scroll,
845 ) -> Option<ScrollResult> {
846 let scroll_location = match scroll {
847 Scroll::Delta(delta) => {
848 let device_pixels_per_page = self.device_pixels_per_page_pixel();
849 let calculate_delta =
850 delta.as_device_vector(device_pixels_per_page) / device_pixels_per_page;
851 ScrollLocation::Delta(calculate_delta.cast_unit())
852 },
853 Scroll::Start => ScrollLocation::Start,
854 Scroll::End => ScrollLocation::End,
855 };
856
857 let hit_test_results: Vec<_> = self
858 .touch_handler
859 .get_hit_test_result_cache_value()
860 .map(|result| vec![result])
861 .unwrap_or_else(|| self.hit_test(render_api, cursor));
862
863 let mut previous_pipeline_id = None;
867 for hit_test_result in hit_test_results {
868 let pipeline_details = self.pipelines.get_mut(&hit_test_result.pipeline_id)?;
869 if previous_pipeline_id.replace(hit_test_result.pipeline_id) !=
870 Some(hit_test_result.pipeline_id)
871 {
872 let scroll_result = pipeline_details.scroll_tree.scroll_node_or_ancestor(
873 hit_test_result.external_scroll_id,
874 scroll_location,
875 ScrollType::InputEvents,
876 );
877 if let Some((external_scroll_id, offset)) = scroll_result {
878 self.touch_handler.set_hit_test_result_cache_value(
884 hit_test_result.clone(),
885 self.device_pixels_per_page_pixel(),
886 );
887 return Some(ScrollResult {
888 hit_test_result,
889 external_scroll_id,
890 offset,
891 });
892 }
893 }
894 }
895 None
896 }
897
898 pub(crate) fn scroll_viewport_by_delta(
902 &mut self,
903 delta: LayoutVector2D,
904 ) -> (PinchZoomResult, Vec<ScrollResult>) {
905 let device_pixels_per_page_pixel = self.device_pixels_per_page_pixel();
906 let delta_in_device_pixels = delta.cast_unit() * device_pixels_per_page_pixel;
907 let remaining = self.pinch_zoom.pan_with_device_scroll(
908 Scroll::Delta(delta_in_device_pixels.into()),
909 device_pixels_per_page_pixel,
910 );
911
912 let pinch_zoom_result = match remaining == delta_in_device_pixels {
913 true => PinchZoomResult::DidNotPinchZoom,
914 false => PinchZoomResult::DidPinchZoom,
915 };
916 if remaining == Vector2D::zero() {
917 return (pinch_zoom_result, vec![]);
918 }
919
920 let Some(root_pipeline_id) = self.root_pipeline_id else {
921 return (pinch_zoom_result, vec![]);
922 };
923 let Some(root_pipeline) = self.pipelines.get_mut(&root_pipeline_id) else {
924 return (pinch_zoom_result, vec![]);
925 };
926
927 let remaining = remaining / device_pixels_per_page_pixel;
928 let Some((external_scroll_id, offset)) = root_pipeline.scroll_tree.scroll_node_or_ancestor(
929 ExternalScrollId(0, root_pipeline_id.into()),
930 ScrollLocation::Delta(remaining.cast_unit()),
931 ScrollType::InputEvents,
933 ) else {
934 return (pinch_zoom_result, vec![]);
935 };
936
937 let hit_test_result = PaintHitTestResult {
938 pipeline_id: root_pipeline_id,
939 point_in_viewport: Default::default(),
942 external_scroll_id,
943 };
944
945 self.send_scroll_positions_to_layout_for_pipeline(root_pipeline_id, external_scroll_id);
946
947 if pinch_zoom_result == PinchZoomResult::DidNotPinchZoom {
948 self.send_pinch_zoom_infos_to_script();
949 }
950
951 let scroll_result = ScrollResult {
952 hit_test_result,
953 external_scroll_id,
954 offset,
955 };
956 (pinch_zoom_result, vec![scroll_result])
957 }
958
959 fn send_pinch_zoom_infos_to_script(&self) {
961 let Some(pipeline_id) = self.root_pipeline_id else {
963 return;
964 };
965
966 let pinch_zoom_infos = self.pinch_zoom.get_pinch_zoom_infos_for_script(
967 self.device_pixels_per_page_pixel_not_including_pinch_zoom(),
968 );
969
970 let _ = self.embedder_to_constellation_sender.send(
971 EmbedderToConstellationMessage::UpdatePinchZoomInfos(pipeline_id, pinch_zoom_infos),
972 );
973 }
974
975 pub(crate) fn pinch_zoom(&self) -> PinchZoom {
976 self.pinch_zoom
977 }
978
979 fn set_pinch_zoom(&mut self, requested_pinch_zoom: PinchZoom) -> PinchZoomResult {
980 if requested_pinch_zoom == self.pinch_zoom {
981 return PinchZoomResult::DidNotPinchZoom;
982 }
983
984 self.pinch_zoom = requested_pinch_zoom;
985 PinchZoomResult::DidPinchZoom
986 }
987
988 pub(crate) fn set_page_zoom(
989 &mut self,
990 new_page_zoom: Scale<f32, CSSPixel, DeviceIndependentPixel>,
991 ) {
992 let new_page_zoom = new_page_zoom.clamp(MIN_PAGE_ZOOM, MAX_PAGE_ZOOM);
993 let old_zoom = std::mem::replace(&mut self.page_zoom, new_page_zoom);
994 if old_zoom != self.page_zoom {
995 self.send_window_size_message();
996 }
997 }
998
999 pub(crate) fn device_pixels_per_page_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> {
1005 let viewport_scale = self
1006 .root_pipeline_id
1007 .and_then(|pipeline_id| self.pipelines.get(&pipeline_id))
1008 .and_then(|pipeline| pipeline.viewport_scale)
1009 .unwrap_or_else(|| self.page_zoom * self.hidpi_scale_factor);
1010 viewport_scale * self.pinch_zoom.zoom_factor()
1011 }
1012
1013 pub(crate) fn device_pixels_per_page_pixel_not_including_pinch_zoom(
1018 &self,
1019 ) -> Scale<f32, CSSPixel, DevicePixel> {
1020 self.page_zoom * self.hidpi_scale_factor
1021 }
1022
1023 pub(crate) fn adjust_pinch_zoom(&mut self, magnification: f32, center: DevicePoint) {
1025 if magnification == 1.0 {
1026 return;
1027 }
1028
1029 self.pending_scroll_zoom_events
1030 .push(ScrollZoomEvent::PinchZoom(magnification, center));
1031 }
1032
1033 fn send_window_size_message(&self) {
1034 let device_pixel_ratio = self.device_pixels_per_page_pixel_not_including_pinch_zoom();
1037 let initial_viewport = self.rect.size().to_f32() / device_pixel_ratio;
1038 let _ = self.embedder_to_constellation_sender.send(
1039 EmbedderToConstellationMessage::ChangeViewportDetails(
1040 self.id,
1041 ViewportDetails {
1042 hidpi_scale_factor: device_pixel_ratio,
1043 size: initial_viewport,
1044 },
1045 WindowSizeType::Resize,
1046 ),
1047 );
1048 }
1049
1050 pub(crate) fn set_hidpi_scale_factor(
1052 &mut self,
1053 new_scale: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1054 ) -> bool {
1055 let old_scale_factor = std::mem::replace(&mut self.hidpi_scale_factor, new_scale);
1056 if self.hidpi_scale_factor == old_scale_factor {
1057 return false;
1058 }
1059
1060 self.send_window_size_message();
1061 true
1062 }
1063
1064 pub(crate) fn set_rect(&mut self, new_rect: DeviceRect) -> bool {
1066 let old_rect = std::mem::replace(&mut self.rect, new_rect);
1067 if old_rect.size() != self.rect.size() {
1068 self.send_window_size_message();
1069 self.pinch_zoom.resize_unscaled_viewport(new_rect);
1070 self.send_pinch_zoom_infos_to_script();
1071 }
1072 old_rect != self.rect
1073 }
1074
1075 pub fn set_viewport_description(&mut self, viewport_description: ViewportDescription) {
1076 self.set_page_zoom(viewport_description.initial_scale);
1077 self.viewport_description = Some(viewport_description);
1078 }
1079
1080 pub(crate) fn scroll_trees_memory_usage(
1081 &self,
1082 ops: &mut malloc_size_of::MallocSizeOfOps,
1083 ) -> usize {
1084 self.pipelines
1085 .values()
1086 .map(|pipeline| pipeline.scroll_tree.size_of(ops))
1087 .sum::<usize>()
1088 }
1089
1090 pub(crate) fn notify_input_event_handled(
1091 &mut self,
1092 render_api: &RenderApi,
1093 repaint_reason: &Cell<RepaintReason>,
1094 id: InputEventId,
1095 result: InputEventResult,
1096 ) {
1097 if let Some(pending_touch_input_event) =
1098 self.touch_handler.take_pending_touch_input_event(id)
1099 {
1100 self.on_touch_event_processed(render_api, pending_touch_input_event, result);
1101 self.touch_handler
1102 .add_touch_move_refresh_observer_if_necessary(
1103 self.refresh_driver.clone(),
1104 repaint_reason,
1105 );
1106 }
1107
1108 if let Some(wheel_event) = self.pending_wheel_events.remove(&id) {
1109 if !result.contains(InputEventResult::DefaultPrevented) {
1110 let scroll_delta =
1112 DeviceVector2D::new(-wheel_event.delta.x as f32, -wheel_event.delta.y as f32);
1113 self.notify_scroll_event(Scroll::Delta(scroll_delta.into()), wheel_event.point);
1114 }
1115 }
1116 }
1117}
1118
1119#[derive(Clone, Copy, Debug, PartialEq)]
1120pub struct UnknownWebView(pub WebViewId);