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,
12 PaintHitTestResult, Scroll, TouchEvent, TouchEventType, ViewportDetails, WebViewPoint,
13 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 PanPolicyInput, PendingTouchInputEvent, TouchHandler, TouchIdMoveTracking, TouchMoveAllowed,
41 TouchSequenceState,
42};
43
44#[derive(Clone, Copy)]
45pub(crate) struct ScrollEvent {
46 pub scroll: Scroll,
48 pub point: DevicePoint,
50 pub scroll_type: ScrollType,
53}
54
55#[derive(Clone, Copy)]
56pub(crate) enum ScrollZoomEvent {
57 PinchZoom(f32, DevicePoint),
60 Scroll(ScrollEvent),
63}
64
65#[derive(Clone, Debug)]
66pub(crate) struct ScrollResult {
67 pub hit_test_result: PaintHitTestResult,
68 pub external_scroll_id: ExternalScrollId,
73 pub offset: LayoutVector2D,
74}
75
76#[derive(Debug, PartialEq)]
77pub(crate) enum PinchZoomResult {
78 DidPinchZoom,
79 DidNotPinchZoom,
80}
81
82pub(crate) struct WebViewRenderer {
86 pub id: WebViewId,
88 pub webview: Box<dyn WebViewTrait>,
92 pub root_pipeline_id: Option<PipelineId>,
94 pub rect: DeviceRect,
96 pub pipelines: FxHashMap<PipelineId, PipelineDetails>,
98 pending_scroll_zoom_events: Vec<ScrollZoomEvent>,
100 pending_wheel_events: FxHashMap<InputEventId, WheelEvent>,
104 touch_handler: TouchHandler,
106 pub page_zoom: Scale<f32, CSSPixel, DeviceIndependentPixel>,
108 pinch_zoom: PinchZoom,
111 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
114 hidden: bool,
116 animating: bool,
119 viewport_description: ViewportDescription,
122
123 screen_size: Size2D<f32, DevicePixel>,
125
126 embedder_to_constellation_sender: Sender<EmbedderToConstellationMessage>,
131 refresh_driver: Rc<BaseRefreshDriver>,
133 webrender_document: DocumentId,
135}
136
137impl WebViewRenderer {
138 pub(crate) fn new(
139 renderer_webview: Box<dyn WebViewTrait>,
140 viewport_details: ViewportDetails,
141 embedder_to_constellation_sender: Sender<EmbedderToConstellationMessage>,
142 refresh_driver: Rc<BaseRefreshDriver>,
143 webrender_document: DocumentId,
144 ) -> Self {
145 let hidpi_scale_factor = viewport_details.hidpi_scale_factor;
146 let size = viewport_details.size * viewport_details.hidpi_scale_factor;
147 let rect = DeviceRect::from_origin_and_size(DevicePoint::origin(), size);
148 let webview_id = renderer_webview.id();
149 Self {
150 id: webview_id,
151 webview: renderer_webview,
152 root_pipeline_id: None,
153 rect,
154 pipelines: Default::default(),
155 touch_handler: TouchHandler::new(webview_id),
156 pending_scroll_zoom_events: Default::default(),
157 pending_wheel_events: Default::default(),
158 page_zoom: DEFAULT_PAGE_ZOOM,
159 pinch_zoom: PinchZoom::new(rect),
160 hidpi_scale_factor: Scale::new(hidpi_scale_factor.0),
161 hidden: false,
162 animating: false,
163 viewport_description: Default::default(),
164 screen_size: viewport_details.device_size,
165 embedder_to_constellation_sender,
166 refresh_driver,
167 webrender_document,
168 }
169 }
170
171 fn hit_test(&self, webrender_api: &RenderApi, point: DevicePoint) -> Vec<PaintHitTestResult> {
172 Painter::hit_test_at_point_with_api_and_document(
173 webrender_api,
174 self.webrender_document,
175 point,
176 )
177 }
178
179 pub(crate) fn animation_callbacks_running(&self) -> bool {
180 self.pipelines
181 .values()
182 .any(PipelineDetails::animation_callbacks_running)
183 }
184
185 pub(crate) fn animating(&self) -> bool {
188 self.animating && !self.hidden()
189 }
190
191 pub(crate) fn hidden(&self) -> bool {
192 self.hidden
193 }
194
195 pub(crate) fn set_hidden(&mut self, new_value: bool) -> bool {
198 let old_value = std::mem::replace(&mut self.hidden, new_value);
199 if new_value == old_value {
200 return false;
201 }
202
203 let _ = self.embedder_to_constellation_sender.send(
204 EmbedderToConstellationMessage::SetWebViewHidden(self.id, new_value),
205 );
206 self.update_animation_state();
207 true
208 }
209
210 pub(crate) fn ensure_pipeline_details(
212 &mut self,
213 pipeline_id: PipelineId,
214 ) -> &mut PipelineDetails {
215 self.pipelines
216 .entry(pipeline_id)
217 .or_insert_with(PipelineDetails::new)
218 }
219
220 pub(crate) fn pipeline_exited(
224 &mut self,
225 pipeline_id: PipelineId,
226 source: PipelineExitSource,
227 ) -> bool {
228 let pipeline = self.pipelines.entry(pipeline_id);
229 let Entry::Occupied(mut pipeline) = pipeline else {
230 return false;
231 };
232
233 pipeline.get_mut().exited.insert(source);
234
235 if !pipeline.get().exited.is_all() {
239 return false;
240 }
241
242 pipeline.get_mut().lcp_candidates.clear();
244
245 pipeline.remove_entry();
246 true
247 }
248
249 pub(crate) fn set_frame_tree(&mut self, frame_tree: &SendableFrameTree) {
250 let pipeline_id = frame_tree.pipeline.id;
251 let old_pipeline_id = self.root_pipeline_id.replace(pipeline_id);
252
253 if old_pipeline_id != self.root_pipeline_id {
254 debug!(
255 "Updating webview ({:?}) from pipeline {:?} to {:?}",
256 3, old_pipeline_id, self.root_pipeline_id
257 );
258 }
259
260 self.set_frame_tree_on_pipeline_details(frame_tree, None);
261 }
262
263 pub(crate) fn send_scroll_positions_to_layout_for_pipeline(
264 &self,
265 pipeline_id: PipelineId,
266 scrolled_node: ExternalScrollId,
267 ) {
268 let Some(details) = self.pipelines.get(&pipeline_id) else {
269 return;
270 };
271
272 let offsets = details.scroll_tree.scroll_offsets();
273
274 if offsets.is_empty() {
279 return;
280 }
281
282 let _ = self.embedder_to_constellation_sender.send(
283 EmbedderToConstellationMessage::SetScrollStates(
284 pipeline_id,
285 ScrollStateUpdate {
286 scrolled_node,
287 offsets,
288 },
289 ),
290 );
291 }
292
293 pub(crate) fn set_frame_tree_on_pipeline_details(
294 &mut self,
295 frame_tree: &SendableFrameTree,
296 parent_pipeline_id: Option<PipelineId>,
297 ) {
298 let pipeline_id = frame_tree.pipeline.id;
299 let pipeline_details = self.ensure_pipeline_details(pipeline_id);
300 pipeline_details.pipeline = Some(frame_tree.pipeline.clone());
301 pipeline_details.parent_pipeline_id = parent_pipeline_id;
302 pipeline_details.children = frame_tree
303 .children
304 .iter()
305 .map(|frame_tree| frame_tree.pipeline.id)
306 .collect();
307
308 for kid in &frame_tree.children {
309 self.set_frame_tree_on_pipeline_details(kid, Some(pipeline_id));
310 }
311 }
312
313 pub(crate) fn change_pipeline_running_animations_state(
316 &mut self,
317 pipeline_id: PipelineId,
318 animation_state: AnimationState,
319 ) -> bool {
320 let pipeline_details = self.ensure_pipeline_details(pipeline_id);
321 let was_animating = pipeline_details.animating();
322 match animation_state {
323 AnimationState::AnimationsPresent => {
324 pipeline_details.animations_running = true;
325 },
326 AnimationState::AnimationCallbacksPresent => {
327 pipeline_details.animation_callbacks_running = true;
328 },
329 AnimationState::NoAnimationsPresent => {
330 pipeline_details.animations_running = false;
331 },
332 AnimationState::AnimationCallbacksAbsent => {
333 pipeline_details.animation_callbacks_running = false;
334 },
335 }
336 let started_animating = !was_animating && pipeline_details.animating();
337
338 self.update_animation_state();
339
340 started_animating
345 }
346
347 pub(crate) fn set_throttled(&mut self, pipeline_id: PipelineId, throttled: bool) -> bool {
350 let pipeline_details = self.ensure_pipeline_details(pipeline_id);
351 let was_animating = pipeline_details.animating();
352 pipeline_details.throttled = throttled;
353 let started_animating = !was_animating && pipeline_details.animating();
354
355 self.update_animation_state();
357
358 started_animating
363 }
364
365 fn update_animation_state(&mut self) {
366 self.animating = self.pipelines.values().any(PipelineDetails::animating);
367 self.webview.set_animating(self.animating());
368 }
369
370 pub(crate) fn for_each_connected_pipeline(&self, callback: &mut impl FnMut(&PipelineDetails)) {
371 if let Some(root_pipeline_id) = self.root_pipeline_id {
372 self.for_each_connected_pipeline_internal(root_pipeline_id, callback);
373 }
374 }
375
376 fn for_each_connected_pipeline_internal(
377 &self,
378 pipeline_id: PipelineId,
379 callback: &mut impl FnMut(&PipelineDetails),
380 ) {
381 let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
382 return;
383 };
384 callback(pipeline);
385 for child_pipeline_id in &pipeline.children {
386 self.for_each_connected_pipeline_internal(*child_pipeline_id, callback);
387 }
388 }
389
390 pub(crate) fn update_touch_handling_at_new_frame_start(&mut self) -> bool {
394 let Some(fling_action) = self.touch_handler.notify_new_frame_start() else {
395 return false;
396 };
397
398 self.on_scroll_window_event(
399 Scroll::Delta((-fling_action.delta).into()),
400 fling_action.cursor,
401 ScrollType::Touch,
404 );
405 true
406 }
407
408 fn dispatch_input_event_with_hit_testing(
409 &mut self,
410 render_api: &RenderApi,
411 event: InputEventAndId,
412 ) -> bool {
413 let event_point = event
414 .event
415 .point()
416 .map(|point| point.as_device_point(self.device_pixels_per_page_pixel()));
417 let is_touch_down = matches!(
418 &event.event,
419 InputEvent::Touch(TouchEvent {
420 event_type: TouchEventType::Down,
421 ..
422 })
423 );
424 let hit_test_result = match event_point {
425 Some(point) => {
426 let hit_test_result = match event.event {
427 InputEvent::Touch(_) => self.touch_handler.get_hit_test_result_cache_value(),
428 _ => None,
429 }
430 .or_else(|| self.hit_test(render_api, point).into_iter().nth(0));
431 if hit_test_result.is_none() {
432 warn!("Empty hit test result for input event, ignoring.");
433 return false;
434 }
435 hit_test_result
436 },
437 None => None,
438 };
439
440 if is_touch_down && let Some(hit) = &hit_test_result {
444 let policy_input = self
445 .pipelines
446 .get(&hit.pipeline_id)
447 .and_then(|pipeline_details| {
448 pipeline_details
449 .scroll_tree
450 .touch_action_and_scrollable_axes_for(hit.external_scroll_id)
451 })
452 .map(
453 |(touch_action, scrollable_x, scrollable_y)| PanPolicyInput {
454 touch_action,
455 scrollable_x,
456 scrollable_y,
457 },
458 );
459 if let Some(input) = policy_input {
460 self.touch_handler.set_pan_policy_input(input);
461 }
462 }
463
464 if let Err(error) = self.embedder_to_constellation_sender.send(
465 EmbedderToConstellationMessage::ForwardInputEvent(self.id, event, hit_test_result),
466 ) {
467 warn!("Sending event to constellation failed ({error:?}).");
468 false
469 } else {
470 true
471 }
472 }
473
474 pub(crate) fn notify_input_event(
475 &mut self,
476 render_api: &RenderApi,
477 repaint_reason: &Cell<RepaintReason>,
478 event_and_id: InputEventAndId,
479 ) -> bool {
480 if let InputEvent::Touch(touch_event) = event_and_id.event {
481 return self.on_touch_event(render_api, repaint_reason, touch_event, event_and_id.id);
482 }
483
484 if let InputEvent::Wheel(wheel_event) = event_and_id.event {
485 self.pending_wheel_events
486 .insert(event_and_id.id, wheel_event);
487 }
488
489 self.dispatch_input_event_with_hit_testing(render_api, event_and_id)
490 }
491
492 fn send_touch_event(
493 &mut self,
494 render_api: &RenderApi,
495 event: TouchEvent,
496 id: InputEventId,
497 ) -> bool {
498 let cancelable = event.is_cancelable();
499 let event_type = event.event_type;
500
501 let input_event_and_id = InputEventAndId {
502 event: InputEvent::Touch(event),
503 id,
504 };
505
506 let result = self.dispatch_input_event_with_hit_testing(render_api, input_event_and_id);
507
508 if cancelable && result {
512 self.touch_handler
513 .add_pending_touch_input_event(id, event.touch_id, event_type);
514 }
515
516 result
517 }
518
519 pub(crate) fn on_touch_event(
520 &mut self,
521 render_api: &RenderApi,
522 repaint_reason: &Cell<RepaintReason>,
523 event: TouchEvent,
524 id: InputEventId,
525 ) -> bool {
526 let result = match event.event_type {
527 TouchEventType::Down => self.on_touch_down(render_api, event, id),
528 TouchEventType::Move => self.on_touch_move(render_api, event, id),
529 TouchEventType::Up => self.on_touch_up(render_api, event, id),
530 TouchEventType::Cancel => self.on_touch_cancel(render_api, event, id),
531 };
532
533 self.touch_handler
534 .add_touch_move_refresh_observer_if_necessary(
535 self.refresh_driver.clone(),
536 repaint_reason,
537 );
538 result
539 }
540
541 fn on_touch_down(
542 &mut self,
543 render_api: &RenderApi,
544 event: TouchEvent,
545 id: InputEventId,
546 ) -> bool {
547 let point = event
548 .point
549 .as_device_point(self.device_pixels_per_page_pixel());
550 self.touch_handler.on_touch_down(event.touch_id, point);
551 self.send_touch_event(render_api, event, id)
552 }
553
554 fn on_touch_move(
555 &mut self,
556 render_api: &RenderApi,
557 mut event: TouchEvent,
558 id: InputEventId,
559 ) -> bool {
560 let point = event
561 .point
562 .as_device_point(self.device_pixels_per_page_pixel());
563 let action = self.touch_handler.on_touch_move(
564 event.touch_id,
565 point,
566 self.device_pixels_per_page_pixel_not_including_pinch_zoom()
567 .get(),
568 );
569 if let Some(action) = action {
570 if self
573 .touch_handler
574 .move_allowed(self.touch_handler.current_sequence_id)
575 {
576 event.disable_cancelable();
578 self.pending_scroll_zoom_events.push(action);
579 }
580 }
581 let mut reached_constellation = false;
582 if !self.touch_handler.is_handling_touch_move_for_touch_id(
586 self.touch_handler.current_sequence_id,
587 event.touch_id,
588 ) {
589 reached_constellation = self.send_touch_event(render_api, event, id);
590 if reached_constellation && event.is_cancelable() {
591 self.touch_handler.set_handling_touch_move_for_touch_id(
592 self.touch_handler.current_sequence_id,
593 event.touch_id,
594 TouchIdMoveTracking::Track,
595 );
596 }
597 }
598 reached_constellation
599 }
600
601 fn on_touch_up(
602 &mut self,
603 render_api: &RenderApi,
604 mut event: TouchEvent,
605 id: InputEventId,
606 ) -> bool {
607 let point = event
608 .point
609 .as_device_point(self.device_pixels_per_page_pixel());
610 if self.touch_handler.on_touch_up(event.touch_id, point) {
612 event.can_synthesize_mouse_events = true;
613 }
614 self.send_touch_event(render_api, event, id)
615 }
616
617 fn on_touch_cancel(
618 &mut self,
619 render_api: &RenderApi,
620 event: TouchEvent,
621 id: InputEventId,
622 ) -> bool {
623 let point = event
624 .point
625 .as_device_point(self.device_pixels_per_page_pixel());
626 self.touch_handler.on_touch_cancel(event.touch_id, point);
627 self.send_touch_event(render_api, event, id)
628 }
629
630 fn on_touch_event_processed(
631 &mut self,
632 pending_touch_input_event: PendingTouchInputEvent,
633 result: InputEventResult,
634 ) {
635 let PendingTouchInputEvent {
636 sequence_id,
637 event_type,
638 touch_id,
639 } = pending_touch_input_event;
640
641 if result.intersects(InputEventResult::DefaultPrevented | InputEventResult::Consumed) {
644 debug!(
645 "Touch event {:?} in sequence {:?} prevented or consumed",
646 event_type, sequence_id
647 );
648 match event_type {
649 TouchEventType::Down => {
650 self.touch_handler.prevent_click(sequence_id);
652 self.touch_handler.prevent_move(sequence_id);
653 self.touch_handler
654 .remove_pending_touch_move_actions(sequence_id);
655 },
656 TouchEventType::Move => {
657 if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) {
659 info.prevent_move = TouchMoveAllowed::Prevented;
660 if let TouchSequenceState::PendingFling { .. } = info.state {
661 info.state = TouchSequenceState::Finished;
662 }
663 self.touch_handler.set_handling_touch_move_for_touch_id(
664 self.touch_handler.current_sequence_id,
665 touch_id,
666 TouchIdMoveTracking::Remove,
667 );
668 self.touch_handler
669 .remove_pending_touch_move_actions(sequence_id);
670 }
671 },
672 TouchEventType::Up => {
673 let Some(info) = &mut self.touch_handler.get_touch_sequence_mut(sequence_id)
677 else {
678 return;
683 };
684 match info.state {
685 TouchSequenceState::PendingClick => {
686 info.state = TouchSequenceState::Finished;
687 self.touch_handler.remove_touch_sequence(sequence_id);
688 },
689 TouchSequenceState::Flinging { .. } => {
690 },
692 TouchSequenceState::Finished => {
693 self.touch_handler.remove_touch_sequence(sequence_id);
694 },
695 TouchSequenceState::Touching |
696 TouchSequenceState::Panning { .. } |
697 TouchSequenceState::Pinching |
698 TouchSequenceState::MultiTouch |
699 TouchSequenceState::PendingFling { .. } => {
700 },
705 }
706 },
707 TouchEventType::Cancel => {
708 self.touch_handler
711 .remove_pending_touch_move_actions(sequence_id);
712 self.touch_handler.try_remove_touch_sequence(sequence_id);
713 },
714 }
715 } else {
716 debug!(
717 "Touch event {:?} in sequence {:?} allowed",
718 event_type, sequence_id
719 );
720 match event_type {
721 TouchEventType::Down => {},
722 TouchEventType::Move => {
723 self.pending_scroll_zoom_events.extend(
724 self.touch_handler
725 .take_pending_touch_move_actions(sequence_id),
726 );
727 self.touch_handler.set_handling_touch_move_for_touch_id(
728 self.touch_handler.current_sequence_id,
729 touch_id,
730 TouchIdMoveTracking::Remove,
731 );
732 if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) &&
733 info.prevent_move == TouchMoveAllowed::Pending
734 {
735 info.prevent_move = TouchMoveAllowed::Allowed;
736 if let TouchSequenceState::PendingFling { velocity, point } = info.state {
737 info.state = TouchSequenceState::Flinging { velocity, point }
738 }
739 }
740 },
741 TouchEventType::Up => {
742 let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) else {
743 return;
745 };
746 match info.state {
747 TouchSequenceState::PendingClick => {
748 info.state = TouchSequenceState::Finished;
749 self.touch_handler.remove_touch_sequence(sequence_id);
750 },
751 TouchSequenceState::Flinging { .. } => {
752 },
754 TouchSequenceState::Finished => {
755 self.touch_handler.remove_touch_sequence(sequence_id);
756 },
757 TouchSequenceState::Panning { .. } |
758 TouchSequenceState::Pinching |
759 TouchSequenceState::PendingFling { .. } => {
760 },
765 TouchSequenceState::MultiTouch | TouchSequenceState::Touching => {
766 },
768 }
769 },
770 TouchEventType::Cancel => {
771 self.touch_handler
772 .remove_pending_touch_move_actions(sequence_id);
773 self.touch_handler.try_remove_touch_sequence(sequence_id);
774 },
775 }
776 }
777 }
778
779 pub(crate) fn notify_scroll_event(&mut self, scroll: Scroll, point: WebViewPoint) {
780 let point = point.as_device_point(self.device_pixels_per_page_pixel());
781 self.on_scroll_window_event(scroll, point, ScrollType::InputEvents);
782 }
783
784 fn on_scroll_window_event(
785 &mut self,
786 scroll: Scroll,
787 cursor: DevicePoint,
788 scroll_type: ScrollType,
789 ) {
790 self.pending_scroll_zoom_events
791 .push(ScrollZoomEvent::Scroll(ScrollEvent {
792 scroll,
793 point: cursor,
794 scroll_type,
795 }));
796 }
797
798 pub(crate) fn process_pending_scroll_and_pinch_zoom_events(
805 &mut self,
806 render_api: &RenderApi,
807 ) -> (PinchZoomResult, Option<ScrollResult>) {
808 if self.pending_scroll_zoom_events.is_empty() {
809 return (PinchZoomResult::DidNotPinchZoom, None);
810 }
811
812 let mut combined_scroll_event: Option<ScrollEvent> = None;
815 let mut new_pinch_zoom = self.pinch_zoom;
816 let device_pixels_per_page_pixel = self.device_pixels_per_page_pixel();
817
818 for scroll_event in self.pending_scroll_zoom_events.drain(..) {
819 match scroll_event {
820 ScrollZoomEvent::PinchZoom(magnification, center) => {
821 let new_factor = self
822 .viewport_description
823 .clamp_zoom(self.pinch_zoom.zoom_factor().0 * magnification);
824 new_pinch_zoom.set_zoom(new_factor, center);
825 },
826 ScrollZoomEvent::Scroll(scroll_event_info) => {
827 let combined_event = match combined_scroll_event.as_mut() {
828 None => {
829 combined_scroll_event = Some(scroll_event_info);
830 continue;
831 },
832 Some(combined_event) => combined_event,
833 };
834
835 match (combined_event.scroll, scroll_event_info.scroll) {
836 (Scroll::Delta(old_delta), Scroll::Delta(new_delta)) => {
837 let old_delta =
838 old_delta.as_device_vector(device_pixels_per_page_pixel);
839 let new_delta =
840 new_delta.as_device_vector(device_pixels_per_page_pixel);
841 combined_event.scroll = Scroll::Delta((old_delta + new_delta).into());
842 },
843 (Scroll::Start, _) | (Scroll::End, _) => {
844 break;
846 },
847 (_, Scroll::Start) | (_, Scroll::End) => {
848 *combined_event = scroll_event_info;
851 break;
852 },
853 }
854 },
855 }
856 }
857
858 if let Some(combined_scroll_event) = combined_scroll_event.as_mut() {
862 new_pinch_zoom.pan(
863 &mut combined_scroll_event.scroll,
864 self.device_pixels_per_page_pixel(),
865 )
866 }
867
868 let scroll_result = combined_scroll_event.and_then(|combined_event| {
869 self.scroll_node_at_device_point(
870 render_api,
871 combined_event.point.to_f32(),
872 combined_event.scroll,
873 combined_event.scroll_type,
874 )
875 });
876 if let Some(ref scroll_result) = scroll_result {
877 self.send_scroll_positions_to_layout_for_pipeline(
878 scroll_result.hit_test_result.pipeline_id,
879 scroll_result.external_scroll_id,
880 );
881 } else {
882 self.touch_handler.stop_fling_if_needed();
883 }
884
885 let pinch_zoom_result = self.set_pinch_zoom(new_pinch_zoom);
887 if pinch_zoom_result == PinchZoomResult::DidPinchZoom {
888 self.send_pinch_zoom_infos_to_script();
889 self.webview.notify_viewport_updated();
893 }
894
895 (pinch_zoom_result, scroll_result)
896 }
897
898 fn scroll_node_at_device_point(
903 &mut self,
904 render_api: &RenderApi,
905 cursor: DevicePoint,
906 scroll: Scroll,
907 scroll_type: ScrollType,
908 ) -> Option<ScrollResult> {
909 let scroll_location = match scroll {
910 Scroll::Delta(delta) => {
911 let device_pixels_per_page = self.device_pixels_per_page_pixel();
912 let calculate_delta =
913 delta.as_device_vector(device_pixels_per_page) / device_pixels_per_page;
914 ScrollLocation::Delta(calculate_delta.cast_unit())
915 },
916 Scroll::Start => ScrollLocation::Start,
917 Scroll::End => ScrollLocation::End,
918 };
919
920 let hit_test_results: Vec<_> = self
921 .touch_handler
922 .get_hit_test_result_cache_value()
923 .map(|result| vec![result])
924 .unwrap_or_else(|| self.hit_test(render_api, cursor));
925
926 let mut previous_pipeline_id = None;
930 for hit_test_result in hit_test_results {
931 let pipeline_details = self.pipelines.get_mut(&hit_test_result.pipeline_id)?;
932 if previous_pipeline_id.replace(hit_test_result.pipeline_id) !=
933 Some(hit_test_result.pipeline_id)
934 {
935 let scroll_result = pipeline_details.scroll_tree.scroll_node_or_ancestor(
936 hit_test_result.external_scroll_id,
937 scroll_location,
938 scroll_type,
939 );
940 if let Some((external_scroll_id, offset)) = scroll_result {
941 self.touch_handler.set_hit_test_result_cache_value(
947 hit_test_result.clone(),
948 self.device_pixels_per_page_pixel(),
949 );
950 return Some(ScrollResult {
951 hit_test_result,
952 external_scroll_id,
953 offset,
954 });
955 }
956 }
957 }
958 None
959 }
960
961 pub(crate) fn scroll_viewport_by_delta(
965 &mut self,
966 delta: LayoutVector2D,
967 ) -> (PinchZoomResult, Vec<ScrollResult>) {
968 let device_pixels_per_page_pixel = self.device_pixels_per_page_pixel();
969 let delta_in_device_pixels = delta.cast_unit() * device_pixels_per_page_pixel;
970 let remaining = self.pinch_zoom.pan_with_device_scroll(
971 Scroll::Delta(delta_in_device_pixels.into()),
972 device_pixels_per_page_pixel,
973 );
974
975 let pinch_zoom_result = match remaining == delta_in_device_pixels {
976 true => PinchZoomResult::DidNotPinchZoom,
977 false => PinchZoomResult::DidPinchZoom,
978 };
979 if remaining == Vector2D::zero() {
980 return (pinch_zoom_result, vec![]);
981 }
982
983 let Some(root_pipeline_id) = self.root_pipeline_id else {
984 return (pinch_zoom_result, vec![]);
985 };
986 let Some(root_pipeline) = self.pipelines.get_mut(&root_pipeline_id) else {
987 return (pinch_zoom_result, vec![]);
988 };
989
990 let remaining = remaining / device_pixels_per_page_pixel;
991 let Some((external_scroll_id, offset)) = root_pipeline.scroll_tree.scroll_node_or_ancestor(
992 ExternalScrollId(0, root_pipeline_id.into()),
993 ScrollLocation::Delta(remaining.cast_unit()),
994 ScrollType::InputEvents,
996 ) else {
997 return (pinch_zoom_result, vec![]);
998 };
999
1000 let hit_test_result = PaintHitTestResult {
1001 pipeline_id: root_pipeline_id,
1002 point_in_viewport: Default::default(),
1005 external_scroll_id,
1006 };
1007
1008 self.send_scroll_positions_to_layout_for_pipeline(root_pipeline_id, external_scroll_id);
1009
1010 if pinch_zoom_result == PinchZoomResult::DidPinchZoom {
1011 self.send_pinch_zoom_infos_to_script();
1012 }
1013
1014 let scroll_result = ScrollResult {
1015 hit_test_result,
1016 external_scroll_id,
1017 offset,
1018 };
1019 (pinch_zoom_result, vec![scroll_result])
1020 }
1021
1022 fn send_pinch_zoom_infos_to_script(&self) {
1024 let Some(pipeline_id) = self.root_pipeline_id else {
1026 return;
1027 };
1028
1029 let pinch_zoom_infos = self.pinch_zoom.get_pinch_zoom_infos_for_script(
1030 self.device_pixels_per_page_pixel_not_including_pinch_zoom(),
1031 );
1032
1033 let _ = self.embedder_to_constellation_sender.send(
1034 EmbedderToConstellationMessage::UpdatePinchZoomInfos(pipeline_id, pinch_zoom_infos),
1035 );
1036 }
1037
1038 pub(crate) fn pinch_zoom(&self) -> PinchZoom {
1039 self.pinch_zoom
1040 }
1041
1042 fn set_pinch_zoom(&mut self, requested_pinch_zoom: PinchZoom) -> PinchZoomResult {
1043 if requested_pinch_zoom == self.pinch_zoom {
1044 return PinchZoomResult::DidNotPinchZoom;
1045 }
1046
1047 self.pinch_zoom = requested_pinch_zoom;
1048 PinchZoomResult::DidPinchZoom
1049 }
1050
1051 pub(crate) fn set_page_zoom(
1053 &mut self,
1054 new_page_zoom: Scale<f32, CSSPixel, DeviceIndependentPixel>,
1055 ) -> bool {
1056 let new_page_zoom = new_page_zoom.clamp(MIN_PAGE_ZOOM, MAX_PAGE_ZOOM);
1057 let old_zoom = std::mem::replace(&mut self.page_zoom, new_page_zoom);
1058 if old_zoom == self.page_zoom {
1059 return false;
1060 }
1061 self.send_window_size_message();
1062 true
1063 }
1064
1065 pub(crate) fn device_pixels_per_page_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> {
1071 let viewport_scale = self
1072 .root_pipeline_id
1073 .and_then(|pipeline_id| self.pipelines.get(&pipeline_id))
1074 .and_then(|pipeline| pipeline.viewport_scale)
1075 .unwrap_or_else(|| self.page_zoom * self.hidpi_scale_factor);
1076 viewport_scale * self.pinch_zoom.zoom_factor()
1077 }
1078
1079 pub(crate) fn device_pixels_per_page_pixel_not_including_pinch_zoom(
1084 &self,
1085 ) -> Scale<f32, CSSPixel, DevicePixel> {
1086 self.page_zoom * self.hidpi_scale_factor
1087 }
1088
1089 pub(crate) fn adjust_pinch_zoom(&mut self, magnification: f32, center: DevicePoint) {
1091 if magnification == 1.0 {
1092 return;
1093 }
1094
1095 self.pending_scroll_zoom_events
1096 .push(ScrollZoomEvent::PinchZoom(magnification, center));
1097 }
1098
1099 fn send_window_size_message(&self) {
1100 let device_pixel_ratio = self.device_pixels_per_page_pixel_not_including_pinch_zoom();
1103 let layout_viewport = self.rect.size().to_f32() /
1106 (device_pixel_ratio * Scale::new(self.viewport_description.initial_scale.get()));
1107 let _ = self.embedder_to_constellation_sender.send(
1108 EmbedderToConstellationMessage::ChangeViewportDetails(
1109 self.id,
1110 ViewportDetails {
1111 hidpi_scale_factor: device_pixel_ratio,
1112 size: layout_viewport,
1113 device_size: self.screen_size,
1114 },
1115 WindowSizeType::Resize,
1116 ),
1117 );
1118 }
1119
1120 pub(crate) fn set_hidpi_scale_factor(
1122 &mut self,
1123 new_scale: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1124 ) -> bool {
1125 let old_scale_factor = std::mem::replace(&mut self.hidpi_scale_factor, new_scale);
1126 if self.hidpi_scale_factor == old_scale_factor {
1127 return false;
1128 }
1129
1130 self.send_window_size_message();
1131 true
1132 }
1133
1134 pub(crate) fn set_screen_size(&mut self, new_size: Size2D<f32, DevicePixel>) -> bool {
1136 if self.screen_size == new_size {
1137 return false;
1138 }
1139 self.screen_size = new_size;
1140
1141 self.send_window_size_message();
1142 true
1143 }
1144
1145 pub(crate) fn set_rect(&mut self, new_rect: DeviceRect) -> bool {
1147 let old_rect = std::mem::replace(&mut self.rect, new_rect);
1148 if old_rect.size() != self.rect.size() {
1149 self.send_window_size_message();
1150 self.pinch_zoom.resize_unscaled_viewport(new_rect);
1151 self.send_pinch_zoom_infos_to_script();
1152 }
1153 old_rect != self.rect
1154 }
1155
1156 pub fn set_viewport_description(&mut self, viewport_description: ViewportDescription) {
1157 self.viewport_description = viewport_description;
1158 self.send_window_size_message();
1159 self.adjust_pinch_zoom(
1160 self.viewport_description.initial_scale.get(),
1161 DevicePoint::origin(),
1162 );
1163 }
1164
1165 pub(crate) fn scroll_trees_memory_usage(
1166 &self,
1167 ops: &mut malloc_size_of::MallocSizeOfOps,
1168 ) -> usize {
1169 self.pipelines
1170 .values()
1171 .map(|pipeline| pipeline.scroll_tree.size_of(ops))
1172 .sum::<usize>()
1173 }
1174
1175 pub(crate) fn notify_input_event_handled(
1176 &mut self,
1177 repaint_reason: &Cell<RepaintReason>,
1178 id: InputEventId,
1179 result: InputEventResult,
1180 ) {
1181 if let Some(pending_touch_input_event) =
1182 self.touch_handler.take_pending_touch_input_event(id)
1183 {
1184 self.on_touch_event_processed(pending_touch_input_event, result);
1185 self.touch_handler
1186 .add_touch_move_refresh_observer_if_necessary(
1187 self.refresh_driver.clone(),
1188 repaint_reason,
1189 );
1190 }
1191
1192 if let Some(wheel_event) = self.pending_wheel_events.remove(&id) &&
1193 !result.contains(InputEventResult::DefaultPrevented)
1194 {
1195 let scroll_delta =
1197 DeviceVector2D::new(-wheel_event.delta.x as f32, -wheel_event.delta.y as f32);
1198 self.notify_scroll_event(Scroll::Delta(scroll_delta.into()), wheel_event.point);
1199 }
1200 }
1201}
1202
1203#[derive(Clone, Copy, Debug, PartialEq)]
1204pub struct UnknownWebView(pub WebViewId);