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 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(&mut self, render_api: &RenderApi, event: TouchEvent, id: InputEventId) -> bool {
602 let point = event
603 .point
604 .as_device_point(self.device_pixels_per_page_pixel());
605 self.touch_handler.on_touch_up(event.touch_id, point);
606 self.send_touch_event(render_api, event, id)
607 }
608
609 fn on_touch_cancel(
610 &mut self,
611 render_api: &RenderApi,
612 event: TouchEvent,
613 id: InputEventId,
614 ) -> bool {
615 let point = event
616 .point
617 .as_device_point(self.device_pixels_per_page_pixel());
618 self.touch_handler.on_touch_cancel(event.touch_id, point);
619 self.send_touch_event(render_api, event, id)
620 }
621
622 fn on_touch_event_processed(
623 &mut self,
624 render_api: &RenderApi,
625 pending_touch_input_event: PendingTouchInputEvent,
626 result: InputEventResult,
627 ) {
628 let PendingTouchInputEvent {
629 sequence_id,
630 event_type,
631 touch_id,
632 } = pending_touch_input_event;
633
634 if result.contains(InputEventResult::DefaultPrevented) {
635 debug!(
636 "Touch event {:?} in sequence {:?} prevented!",
637 event_type, sequence_id
638 );
639 match event_type {
640 TouchEventType::Down => {
641 self.touch_handler.prevent_click(sequence_id);
643 self.touch_handler.prevent_move(sequence_id);
644 self.touch_handler
645 .remove_pending_touch_move_actions(sequence_id);
646 },
647 TouchEventType::Move => {
648 if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) {
650 info.prevent_move = TouchMoveAllowed::Prevented;
651 if let TouchSequenceState::PendingFling { .. } = info.state {
652 info.state = TouchSequenceState::Finished;
653 }
654 self.touch_handler.set_handling_touch_move_for_touch_id(
655 self.touch_handler.current_sequence_id,
656 touch_id,
657 TouchIdMoveTracking::Remove,
658 );
659 self.touch_handler
660 .remove_pending_touch_move_actions(sequence_id);
661 }
662 },
663 TouchEventType::Up => {
664 let Some(info) = &mut self.touch_handler.get_touch_sequence_mut(sequence_id)
668 else {
669 return;
674 };
675 match info.state {
676 TouchSequenceState::PendingClick(_) => {
677 info.state = TouchSequenceState::Finished;
678 self.touch_handler.remove_touch_sequence(sequence_id);
679 },
680 TouchSequenceState::Flinging { .. } => {
681 },
683 TouchSequenceState::Finished => {
684 self.touch_handler.remove_touch_sequence(sequence_id);
685 },
686 TouchSequenceState::Touching |
687 TouchSequenceState::Panning { .. } |
688 TouchSequenceState::Pinching |
689 TouchSequenceState::MultiTouch |
690 TouchSequenceState::PendingFling { .. } => {
691 },
696 }
697 },
698 TouchEventType::Cancel => {
699 self.touch_handler
702 .remove_pending_touch_move_actions(sequence_id);
703 self.touch_handler.try_remove_touch_sequence(sequence_id);
704 },
705 }
706 } else {
707 debug!(
708 "Touch event {:?} in sequence {:?} allowed",
709 event_type, sequence_id
710 );
711 match event_type {
712 TouchEventType::Down => {},
713 TouchEventType::Move => {
714 self.pending_scroll_zoom_events.extend(
715 self.touch_handler
716 .take_pending_touch_move_actions(sequence_id),
717 );
718 self.touch_handler.set_handling_touch_move_for_touch_id(
719 self.touch_handler.current_sequence_id,
720 touch_id,
721 TouchIdMoveTracking::Remove,
722 );
723 if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) &&
724 info.prevent_move == TouchMoveAllowed::Pending
725 {
726 info.prevent_move = TouchMoveAllowed::Allowed;
727 if let TouchSequenceState::PendingFling { velocity, point } = info.state {
728 info.state = TouchSequenceState::Flinging { velocity, point }
729 }
730 }
731 },
732 TouchEventType::Up => {
733 let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) else {
734 return;
736 };
737 match info.state {
738 TouchSequenceState::PendingClick(point) => {
739 info.state = TouchSequenceState::Finished;
740 if !info.prevent_click {
743 self.simulate_mouse_click(render_api, point);
744 }
745 self.touch_handler.remove_touch_sequence(sequence_id);
746 },
747 TouchSequenceState::Flinging { .. } => {
748 },
750 TouchSequenceState::Finished => {
751 self.touch_handler.remove_touch_sequence(sequence_id);
752 },
753 TouchSequenceState::Panning { .. } |
754 TouchSequenceState::Pinching |
755 TouchSequenceState::PendingFling { .. } => {
756 },
761 TouchSequenceState::MultiTouch | TouchSequenceState::Touching => {
762 },
764 }
765 },
766 TouchEventType::Cancel => {
767 self.touch_handler
768 .remove_pending_touch_move_actions(sequence_id);
769 self.touch_handler.try_remove_touch_sequence(sequence_id);
770 },
771 }
772 }
773 }
774
775 fn simulate_mouse_click(&mut self, render_api: &RenderApi, point: DevicePoint) {
777 let button = MouseButton::Primary;
778 self.dispatch_input_event_with_hit_testing(
779 render_api,
780 InputEvent::MouseMove(MouseMoveEvent::new_compatibility_for_touch(point.into())).into(),
781 );
782 self.dispatch_input_event_with_hit_testing(
783 render_api,
784 InputEvent::MouseButton(MouseButtonEvent::new(
785 MouseButtonAction::Down,
786 button,
787 point.into(),
788 ))
789 .into(),
790 );
791 self.dispatch_input_event_with_hit_testing(
792 render_api,
793 InputEvent::MouseButton(MouseButtonEvent::new(
794 MouseButtonAction::Up,
795 button,
796 point.into(),
797 ))
798 .into(),
799 );
800 }
801
802 pub(crate) fn notify_scroll_event(&mut self, scroll: Scroll, point: WebViewPoint) {
803 let point = point.as_device_point(self.device_pixels_per_page_pixel());
804 self.on_scroll_window_event(scroll, point, ScrollType::InputEvents);
805 }
806
807 fn on_scroll_window_event(
808 &mut self,
809 scroll: Scroll,
810 cursor: DevicePoint,
811 scroll_type: ScrollType,
812 ) {
813 self.pending_scroll_zoom_events
814 .push(ScrollZoomEvent::Scroll(ScrollEvent {
815 scroll,
816 point: cursor,
817 scroll_type,
818 }));
819 }
820
821 pub(crate) fn process_pending_scroll_and_pinch_zoom_events(
828 &mut self,
829 render_api: &RenderApi,
830 ) -> (PinchZoomResult, Option<ScrollResult>) {
831 if self.pending_scroll_zoom_events.is_empty() {
832 return (PinchZoomResult::DidNotPinchZoom, None);
833 }
834
835 let mut combined_scroll_event: Option<ScrollEvent> = None;
838 let mut new_pinch_zoom = self.pinch_zoom;
839 let device_pixels_per_page_pixel = self.device_pixels_per_page_pixel();
840
841 for scroll_event in self.pending_scroll_zoom_events.drain(..) {
842 match scroll_event {
843 ScrollZoomEvent::PinchZoom(magnification, center) => {
844 let new_factor = self
845 .viewport_description
846 .clamp_zoom(self.pinch_zoom.zoom_factor().0 * magnification);
847 new_pinch_zoom.set_zoom(new_factor, center);
848 },
849 ScrollZoomEvent::Scroll(scroll_event_info) => {
850 let combined_event = match combined_scroll_event.as_mut() {
851 None => {
852 combined_scroll_event = Some(scroll_event_info);
853 continue;
854 },
855 Some(combined_event) => combined_event,
856 };
857
858 match (combined_event.scroll, scroll_event_info.scroll) {
859 (Scroll::Delta(old_delta), Scroll::Delta(new_delta)) => {
860 let old_delta =
861 old_delta.as_device_vector(device_pixels_per_page_pixel);
862 let new_delta =
863 new_delta.as_device_vector(device_pixels_per_page_pixel);
864 combined_event.scroll = Scroll::Delta((old_delta + new_delta).into());
865 },
866 (Scroll::Start, _) | (Scroll::End, _) => {
867 break;
869 },
870 (_, Scroll::Start) | (_, Scroll::End) => {
871 *combined_event = scroll_event_info;
874 break;
875 },
876 }
877 },
878 }
879 }
880
881 if let Some(combined_scroll_event) = combined_scroll_event.as_mut() {
885 new_pinch_zoom.pan(
886 &mut combined_scroll_event.scroll,
887 self.device_pixels_per_page_pixel(),
888 )
889 }
890
891 let scroll_result = combined_scroll_event.and_then(|combined_event| {
892 self.scroll_node_at_device_point(
893 render_api,
894 combined_event.point.to_f32(),
895 combined_event.scroll,
896 combined_event.scroll_type,
897 )
898 });
899 if let Some(ref scroll_result) = scroll_result {
900 self.send_scroll_positions_to_layout_for_pipeline(
901 scroll_result.hit_test_result.pipeline_id,
902 scroll_result.external_scroll_id,
903 );
904 } else {
905 self.touch_handler.stop_fling_if_needed();
906 }
907
908 let pinch_zoom_result = self.set_pinch_zoom(new_pinch_zoom);
910 if pinch_zoom_result == PinchZoomResult::DidPinchZoom {
911 self.send_pinch_zoom_infos_to_script();
912 self.webview.notify_viewport_updated();
916 }
917
918 (pinch_zoom_result, scroll_result)
919 }
920
921 fn scroll_node_at_device_point(
926 &mut self,
927 render_api: &RenderApi,
928 cursor: DevicePoint,
929 scroll: Scroll,
930 scroll_type: ScrollType,
931 ) -> Option<ScrollResult> {
932 let scroll_location = match scroll {
933 Scroll::Delta(delta) => {
934 let device_pixels_per_page = self.device_pixels_per_page_pixel();
935 let calculate_delta =
936 delta.as_device_vector(device_pixels_per_page) / device_pixels_per_page;
937 ScrollLocation::Delta(calculate_delta.cast_unit())
938 },
939 Scroll::Start => ScrollLocation::Start,
940 Scroll::End => ScrollLocation::End,
941 };
942
943 let hit_test_results: Vec<_> = self
944 .touch_handler
945 .get_hit_test_result_cache_value()
946 .map(|result| vec![result])
947 .unwrap_or_else(|| self.hit_test(render_api, cursor));
948
949 let mut previous_pipeline_id = None;
953 for hit_test_result in hit_test_results {
954 let pipeline_details = self.pipelines.get_mut(&hit_test_result.pipeline_id)?;
955 if previous_pipeline_id.replace(hit_test_result.pipeline_id) !=
956 Some(hit_test_result.pipeline_id)
957 {
958 let scroll_result = pipeline_details.scroll_tree.scroll_node_or_ancestor(
959 hit_test_result.external_scroll_id,
960 scroll_location,
961 scroll_type,
962 );
963 if let Some((external_scroll_id, offset)) = scroll_result {
964 self.touch_handler.set_hit_test_result_cache_value(
970 hit_test_result.clone(),
971 self.device_pixels_per_page_pixel(),
972 );
973 return Some(ScrollResult {
974 hit_test_result,
975 external_scroll_id,
976 offset,
977 });
978 }
979 }
980 }
981 None
982 }
983
984 pub(crate) fn scroll_viewport_by_delta(
988 &mut self,
989 delta: LayoutVector2D,
990 ) -> (PinchZoomResult, Vec<ScrollResult>) {
991 let device_pixels_per_page_pixel = self.device_pixels_per_page_pixel();
992 let delta_in_device_pixels = delta.cast_unit() * device_pixels_per_page_pixel;
993 let remaining = self.pinch_zoom.pan_with_device_scroll(
994 Scroll::Delta(delta_in_device_pixels.into()),
995 device_pixels_per_page_pixel,
996 );
997
998 let pinch_zoom_result = match remaining == delta_in_device_pixels {
999 true => PinchZoomResult::DidNotPinchZoom,
1000 false => PinchZoomResult::DidPinchZoom,
1001 };
1002 if remaining == Vector2D::zero() {
1003 return (pinch_zoom_result, vec![]);
1004 }
1005
1006 let Some(root_pipeline_id) = self.root_pipeline_id else {
1007 return (pinch_zoom_result, vec![]);
1008 };
1009 let Some(root_pipeline) = self.pipelines.get_mut(&root_pipeline_id) else {
1010 return (pinch_zoom_result, vec![]);
1011 };
1012
1013 let remaining = remaining / device_pixels_per_page_pixel;
1014 let Some((external_scroll_id, offset)) = root_pipeline.scroll_tree.scroll_node_or_ancestor(
1015 ExternalScrollId(0, root_pipeline_id.into()),
1016 ScrollLocation::Delta(remaining.cast_unit()),
1017 ScrollType::InputEvents,
1019 ) else {
1020 return (pinch_zoom_result, vec![]);
1021 };
1022
1023 let hit_test_result = PaintHitTestResult {
1024 pipeline_id: root_pipeline_id,
1025 point_in_viewport: Default::default(),
1028 external_scroll_id,
1029 };
1030
1031 self.send_scroll_positions_to_layout_for_pipeline(root_pipeline_id, external_scroll_id);
1032
1033 if pinch_zoom_result == PinchZoomResult::DidPinchZoom {
1034 self.send_pinch_zoom_infos_to_script();
1035 }
1036
1037 let scroll_result = ScrollResult {
1038 hit_test_result,
1039 external_scroll_id,
1040 offset,
1041 };
1042 (pinch_zoom_result, vec![scroll_result])
1043 }
1044
1045 fn send_pinch_zoom_infos_to_script(&self) {
1047 let Some(pipeline_id) = self.root_pipeline_id else {
1049 return;
1050 };
1051
1052 let pinch_zoom_infos = self.pinch_zoom.get_pinch_zoom_infos_for_script(
1053 self.device_pixels_per_page_pixel_not_including_pinch_zoom(),
1054 );
1055
1056 let _ = self.embedder_to_constellation_sender.send(
1057 EmbedderToConstellationMessage::UpdatePinchZoomInfos(pipeline_id, pinch_zoom_infos),
1058 );
1059 }
1060
1061 pub(crate) fn pinch_zoom(&self) -> PinchZoom {
1062 self.pinch_zoom
1063 }
1064
1065 fn set_pinch_zoom(&mut self, requested_pinch_zoom: PinchZoom) -> PinchZoomResult {
1066 if requested_pinch_zoom == self.pinch_zoom {
1067 return PinchZoomResult::DidNotPinchZoom;
1068 }
1069
1070 self.pinch_zoom = requested_pinch_zoom;
1071 PinchZoomResult::DidPinchZoom
1072 }
1073
1074 pub(crate) fn set_page_zoom(
1076 &mut self,
1077 new_page_zoom: Scale<f32, CSSPixel, DeviceIndependentPixel>,
1078 ) -> bool {
1079 let new_page_zoom = new_page_zoom.clamp(MIN_PAGE_ZOOM, MAX_PAGE_ZOOM);
1080 let old_zoom = std::mem::replace(&mut self.page_zoom, new_page_zoom);
1081 if old_zoom == self.page_zoom {
1082 return false;
1083 }
1084 self.send_window_size_message();
1085 true
1086 }
1087
1088 pub(crate) fn device_pixels_per_page_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> {
1094 let viewport_scale = self
1095 .root_pipeline_id
1096 .and_then(|pipeline_id| self.pipelines.get(&pipeline_id))
1097 .and_then(|pipeline| pipeline.viewport_scale)
1098 .unwrap_or_else(|| self.page_zoom * self.hidpi_scale_factor);
1099 viewport_scale * self.pinch_zoom.zoom_factor()
1100 }
1101
1102 pub(crate) fn device_pixels_per_page_pixel_not_including_pinch_zoom(
1107 &self,
1108 ) -> Scale<f32, CSSPixel, DevicePixel> {
1109 self.page_zoom * self.hidpi_scale_factor
1110 }
1111
1112 pub(crate) fn adjust_pinch_zoom(&mut self, magnification: f32, center: DevicePoint) {
1114 if magnification == 1.0 {
1115 return;
1116 }
1117
1118 self.pending_scroll_zoom_events
1119 .push(ScrollZoomEvent::PinchZoom(magnification, center));
1120 }
1121
1122 fn send_window_size_message(&self) {
1123 let device_pixel_ratio = self.device_pixels_per_page_pixel_not_including_pinch_zoom();
1126 let layout_viewport = self.rect.size().to_f32() /
1129 (device_pixel_ratio * Scale::new(self.viewport_description.initial_scale.get()));
1130 let _ = self.embedder_to_constellation_sender.send(
1131 EmbedderToConstellationMessage::ChangeViewportDetails(
1132 self.id,
1133 ViewportDetails {
1134 hidpi_scale_factor: device_pixel_ratio,
1135 size: layout_viewport,
1136 device_size: self.screen_size,
1137 },
1138 WindowSizeType::Resize,
1139 ),
1140 );
1141 }
1142
1143 pub(crate) fn set_hidpi_scale_factor(
1145 &mut self,
1146 new_scale: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1147 ) -> bool {
1148 let old_scale_factor = std::mem::replace(&mut self.hidpi_scale_factor, new_scale);
1149 if self.hidpi_scale_factor == old_scale_factor {
1150 return false;
1151 }
1152
1153 self.send_window_size_message();
1154 true
1155 }
1156
1157 pub(crate) fn set_screen_size(&mut self, new_size: Size2D<f32, DevicePixel>) -> bool {
1159 if self.screen_size == new_size {
1160 return false;
1161 }
1162 self.screen_size = new_size;
1163
1164 self.send_window_size_message();
1165 true
1166 }
1167
1168 pub(crate) fn set_rect(&mut self, new_rect: DeviceRect) -> bool {
1170 let old_rect = std::mem::replace(&mut self.rect, new_rect);
1171 if old_rect.size() != self.rect.size() {
1172 self.send_window_size_message();
1173 self.pinch_zoom.resize_unscaled_viewport(new_rect);
1174 self.send_pinch_zoom_infos_to_script();
1175 }
1176 old_rect != self.rect
1177 }
1178
1179 pub fn set_viewport_description(&mut self, viewport_description: ViewportDescription) {
1180 self.viewport_description = viewport_description;
1181 self.send_window_size_message();
1182 self.adjust_pinch_zoom(
1183 self.viewport_description.initial_scale.get(),
1184 DevicePoint::origin(),
1185 );
1186 }
1187
1188 pub(crate) fn scroll_trees_memory_usage(
1189 &self,
1190 ops: &mut malloc_size_of::MallocSizeOfOps,
1191 ) -> usize {
1192 self.pipelines
1193 .values()
1194 .map(|pipeline| pipeline.scroll_tree.size_of(ops))
1195 .sum::<usize>()
1196 }
1197
1198 pub(crate) fn notify_input_event_handled(
1199 &mut self,
1200 render_api: &RenderApi,
1201 repaint_reason: &Cell<RepaintReason>,
1202 id: InputEventId,
1203 result: InputEventResult,
1204 ) {
1205 if let Some(pending_touch_input_event) =
1206 self.touch_handler.take_pending_touch_input_event(id)
1207 {
1208 self.on_touch_event_processed(render_api, pending_touch_input_event, result);
1209 self.touch_handler
1210 .add_touch_move_refresh_observer_if_necessary(
1211 self.refresh_driver.clone(),
1212 repaint_reason,
1213 );
1214 }
1215
1216 if let Some(wheel_event) = self.pending_wheel_events.remove(&id) &&
1217 !result.contains(InputEventResult::DefaultPrevented)
1218 {
1219 let scroll_delta =
1221 DeviceVector2D::new(-wheel_event.delta.x as f32, -wheel_event.delta.y as f32);
1222 self.notify_scroll_event(Scroll::Delta(scroll_delta.into()), wheel_event.point);
1223 }
1224 }
1225}
1226
1227#[derive(Clone, Copy, Debug, PartialEq)]
1228pub struct UnknownWebView(pub WebViewId);