1use std::cell::Cell;
6use std::collections::hash_map::Entry;
7use std::rc::Rc;
8
9use base::id::{PipelineId, WebViewId};
10use constellation_traits::{EmbedderToConstellationMessage, WindowSizeType};
11use crossbeam_channel::Sender;
12use embedder_traits::{
13 AnimationState, InputEvent, InputEventAndId, InputEventId, InputEventResult, MouseButton,
14 MouseButtonAction, MouseButtonEvent, MouseMoveEvent, PaintHitTestResult, Scroll,
15 ScrollEvent as EmbedderScrollEvent, TouchEvent, TouchEventType, ViewportDetails, WebViewPoint,
16 WheelEvent,
17};
18use euclid::{Scale, Vector2D};
19use log::{debug, warn};
20use malloc_size_of::MallocSizeOf;
21use paint_api::display_list::ScrollType;
22use paint_api::viewport_description::{
23 DEFAULT_PAGE_ZOOM, MAX_PAGE_ZOOM, MIN_PAGE_ZOOM, ViewportDescription,
24};
25use paint_api::{PipelineExitSource, SendableFrameTree, WebViewTrait};
26use rustc_hash::FxHashMap;
27use servo_geometry::DeviceIndependentPixel;
28use style_traits::CSSPixel;
29use webrender::RenderApi;
30use webrender_api::units::{DevicePixel, DevicePoint, DeviceRect, DeviceVector2D, LayoutVector2D};
31use webrender_api::{DocumentId, ExternalScrollId, ScrollLocation};
32
33use crate::paint::RepaintReason;
34use crate::painter::Painter;
35use crate::pinch_zoom::PinchZoom;
36use crate::pipeline_details::PipelineDetails;
37use crate::refresh_driver::BaseRefreshDriver;
38use crate::touch::{PendingTouchInputEvent, TouchHandler, TouchMoveAllowed, TouchSequenceState};
39
40#[derive(Clone, Copy)]
41pub(crate) struct ScrollEvent {
42 pub scroll: Scroll,
44 pub point: DevicePoint,
46 pub event_count: u32,
48}
49
50#[derive(Clone, Copy)]
51pub(crate) enum ScrollZoomEvent {
52 PinchZoom(f32, DevicePoint),
55 Scroll(ScrollEvent),
58}
59
60#[derive(Clone, Debug)]
61pub(crate) struct ScrollResult {
62 pub hit_test_result: PaintHitTestResult,
63 pub external_scroll_id: ExternalScrollId,
68 pub offset: LayoutVector2D,
69}
70
71#[derive(Debug, PartialEq)]
72pub(crate) enum PinchZoomResult {
73 DidPinchZoom,
74 DidNotPinchZoom,
75}
76
77pub(crate) struct WebViewRenderer {
81 pub id: WebViewId,
83 pub webview: Box<dyn WebViewTrait>,
87 pub root_pipeline_id: Option<PipelineId>,
89 pub rect: DeviceRect,
91 pub pipelines: FxHashMap<PipelineId, PipelineDetails>,
93 pending_scroll_zoom_events: Vec<ScrollZoomEvent>,
95 pending_wheel_events: FxHashMap<InputEventId, WheelEvent>,
99 touch_handler: TouchHandler,
101 pub page_zoom: Scale<f32, CSSPixel, DeviceIndependentPixel>,
103 pinch_zoom: PinchZoom,
106 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
109 hidden: bool,
111 animating: bool,
114 viewport_description: Option<ViewportDescription>,
117
118 embedder_to_constellation_sender: Sender<EmbedderToConstellationMessage>,
123 refresh_driver: Rc<BaseRefreshDriver>,
125 webrender_document: DocumentId,
127}
128
129impl WebViewRenderer {
130 pub(crate) fn new(
131 renderer_webview: Box<dyn WebViewTrait>,
132 viewport_details: ViewportDetails,
133 embedder_to_constellation_sender: Sender<EmbedderToConstellationMessage>,
134 refresh_driver: Rc<BaseRefreshDriver>,
135 webrender_document: DocumentId,
136 ) -> Self {
137 let hidpi_scale_factor = viewport_details.hidpi_scale_factor;
138 let size = viewport_details.size * viewport_details.hidpi_scale_factor;
139 let rect = DeviceRect::from_origin_and_size(DevicePoint::origin(), size);
140 let webview_id = renderer_webview.id();
141 Self {
142 id: webview_id,
143 webview: renderer_webview,
144 root_pipeline_id: None,
145 rect,
146 pipelines: Default::default(),
147 touch_handler: TouchHandler::new(webview_id),
148 pending_scroll_zoom_events: Default::default(),
149 pending_wheel_events: Default::default(),
150 page_zoom: DEFAULT_PAGE_ZOOM,
151 pinch_zoom: PinchZoom::new(rect),
152 hidpi_scale_factor: Scale::new(hidpi_scale_factor.0),
153 hidden: false,
154 animating: false,
155 viewport_description: None,
156 embedder_to_constellation_sender,
157 refresh_driver,
158 webrender_document,
159 }
160 }
161
162 fn hit_test(&self, webrender_api: &RenderApi, point: DevicePoint) -> Vec<PaintHitTestResult> {
163 Painter::hit_test_at_point_with_api_and_document(
164 webrender_api,
165 self.webrender_document,
166 point,
167 )
168 }
169
170 pub(crate) fn animation_callbacks_running(&self) -> bool {
171 self.pipelines
172 .values()
173 .any(PipelineDetails::animation_callbacks_running)
174 }
175
176 pub(crate) fn animating(&self) -> bool {
177 self.animating
178 }
179
180 pub(crate) fn hidden(&self) -> bool {
181 self.hidden
182 }
183
184 pub(crate) fn set_hidden(&mut self, new_value: bool) -> bool {
187 let old_value = std::mem::replace(&mut self.hidden, new_value);
188 new_value != old_value
189 }
190
191 pub(crate) fn ensure_pipeline_details(
193 &mut self,
194 pipeline_id: PipelineId,
195 ) -> &mut PipelineDetails {
196 self.pipelines
197 .entry(pipeline_id)
198 .or_insert_with(PipelineDetails::new)
199 }
200
201 pub(crate) fn pipeline_exited(&mut self, pipeline_id: PipelineId, source: PipelineExitSource) {
202 let pipeline = self.pipelines.entry(pipeline_id);
203 let Entry::Occupied(mut pipeline) = pipeline else {
204 return;
205 };
206
207 pipeline.get_mut().exited.insert(source);
208
209 if !pipeline.get().exited.is_all() {
213 return;
214 }
215
216 pipeline.remove_entry();
217 }
218
219 pub(crate) fn set_frame_tree(&mut self, frame_tree: &SendableFrameTree) {
220 let pipeline_id = frame_tree.pipeline.id;
221 let old_pipeline_id = self.root_pipeline_id.replace(pipeline_id);
222
223 if old_pipeline_id != self.root_pipeline_id {
224 debug!(
225 "Updating webview ({:?}) from pipeline {:?} to {:?}",
226 3, old_pipeline_id, self.root_pipeline_id
227 );
228 }
229
230 self.set_frame_tree_on_pipeline_details(frame_tree, None);
231 }
232
233 pub(crate) fn send_scroll_positions_to_layout_for_pipeline(&self, pipeline_id: PipelineId) {
234 let Some(details) = self.pipelines.get(&pipeline_id) else {
235 return;
236 };
237
238 let scroll_offsets = details.scroll_tree.scroll_offsets();
239
240 if scroll_offsets.is_empty() {
245 return;
246 }
247
248 let _ = self.embedder_to_constellation_sender.send(
249 EmbedderToConstellationMessage::SetScrollStates(pipeline_id, scroll_offsets),
250 );
251 }
252
253 pub(crate) fn set_frame_tree_on_pipeline_details(
254 &mut self,
255 frame_tree: &SendableFrameTree,
256 parent_pipeline_id: Option<PipelineId>,
257 ) {
258 let pipeline_id = frame_tree.pipeline.id;
259 let pipeline_details = self.ensure_pipeline_details(pipeline_id);
260 pipeline_details.pipeline = Some(frame_tree.pipeline.clone());
261 pipeline_details.parent_pipeline_id = parent_pipeline_id;
262
263 for kid in &frame_tree.children {
264 self.set_frame_tree_on_pipeline_details(kid, Some(pipeline_id));
265 }
266 }
267
268 pub(crate) fn change_pipeline_running_animations_state(
271 &mut self,
272 pipeline_id: PipelineId,
273 animation_state: AnimationState,
274 ) -> bool {
275 let pipeline_details = self.ensure_pipeline_details(pipeline_id);
276 let was_animating = pipeline_details.animating();
277 match animation_state {
278 AnimationState::AnimationsPresent => {
279 pipeline_details.animations_running = true;
280 },
281 AnimationState::AnimationCallbacksPresent => {
282 pipeline_details.animation_callbacks_running = true;
283 },
284 AnimationState::NoAnimationsPresent => {
285 pipeline_details.animations_running = false;
286 },
287 AnimationState::NoAnimationCallbacksPresent => {
288 pipeline_details.animation_callbacks_running = false;
289 },
290 }
291 let started_animating = !was_animating && pipeline_details.animating();
292
293 self.update_animation_state();
294
295 started_animating
300 }
301
302 pub(crate) fn set_throttled(&mut self, pipeline_id: PipelineId, throttled: bool) -> bool {
305 let pipeline_details = self.ensure_pipeline_details(pipeline_id);
306 let was_animating = pipeline_details.animating();
307 pipeline_details.throttled = throttled;
308 let started_animating = !was_animating && pipeline_details.animating();
309
310 self.update_animation_state();
312
313 started_animating
318 }
319
320 fn update_animation_state(&mut self) {
321 self.animating = self.pipelines.values().any(PipelineDetails::animating);
322 self.webview.set_animating(self.animating());
323 }
324
325 pub(crate) fn update_touch_handling_at_new_frame_start(&mut self) -> bool {
329 let Some(fling_action) = self.touch_handler.notify_new_frame_start() else {
330 return false;
331 };
332
333 self.on_scroll_window_event(
334 Scroll::Delta((-fling_action.delta).into()),
335 fling_action.cursor,
336 );
337 true
338 }
339
340 fn dispatch_input_event_with_hit_testing(
341 &mut self,
342 render_api: &RenderApi,
343 event: InputEventAndId,
344 ) -> bool {
345 let event_point = event
346 .event
347 .point()
348 .map(|point| point.as_device_point(self.device_pixels_per_page_pixel()));
349 let hit_test_result = match event_point {
350 Some(point) => {
351 let hit_test_result = match event.event {
352 InputEvent::Touch(_) => self.touch_handler.get_hit_test_result_cache_value(),
353 _ => None,
354 }
355 .or_else(|| self.hit_test(render_api, point).into_iter().nth(0));
356 if hit_test_result.is_none() {
357 warn!("Empty hit test result for input event, ignoring.");
358 return false;
359 }
360 hit_test_result
361 },
362 None => None,
363 };
364
365 if let Err(error) = self.embedder_to_constellation_sender.send(
366 EmbedderToConstellationMessage::ForwardInputEvent(self.id, event, hit_test_result),
367 ) {
368 warn!("Sending event to constellation failed ({error:?}).");
369 false
370 } else {
371 true
372 }
373 }
374
375 pub(crate) fn notify_input_event(
376 &mut self,
377 render_api: &RenderApi,
378 repaint_reason: &Cell<RepaintReason>,
379 event_and_id: InputEventAndId,
380 ) {
381 if let InputEvent::Touch(touch_event) = event_and_id.event {
382 self.on_touch_event(render_api, repaint_reason, touch_event, event_and_id.id);
383 return;
384 }
385
386 if let InputEvent::Wheel(wheel_event) = event_and_id.event {
387 self.pending_wheel_events
388 .insert(event_and_id.id, wheel_event);
389 }
390
391 self.dispatch_input_event_with_hit_testing(render_api, event_and_id);
392 }
393
394 fn send_touch_event(
395 &mut self,
396 render_api: &RenderApi,
397 event: TouchEvent,
398 id: InputEventId,
399 ) -> bool {
400 let cancelable = event.is_cancelable();
401 let event_type = event.event_type;
402
403 let input_event_and_id = InputEventAndId {
404 event: InputEvent::Touch(event),
405 id,
406 };
407
408 let result = self.dispatch_input_event_with_hit_testing(render_api, input_event_and_id);
409
410 if cancelable && result {
414 self.touch_handler
415 .add_pending_touch_input_event(id, event.touch_id, event_type);
416 }
417
418 result
419 }
420
421 pub(crate) fn on_touch_event(
422 &mut self,
423 render_api: &RenderApi,
424 repaint_reason: &Cell<RepaintReason>,
425 event: TouchEvent,
426 id: InputEventId,
427 ) {
428 match event.event_type {
429 TouchEventType::Down => self.on_touch_down(render_api, event, id),
430 TouchEventType::Move => self.on_touch_move(render_api, event, id),
431 TouchEventType::Up => self.on_touch_up(render_api, event, id),
432 TouchEventType::Cancel => self.on_touch_cancel(render_api, event, id),
433 }
434
435 self.touch_handler
436 .add_touch_move_refresh_observer_if_necessary(
437 self.refresh_driver.clone(),
438 repaint_reason,
439 );
440 }
441
442 fn on_touch_down(&mut self, render_api: &RenderApi, event: TouchEvent, id: InputEventId) {
443 let point = event
444 .point
445 .as_device_point(self.device_pixels_per_page_pixel());
446 self.touch_handler.on_touch_down(event.touch_id, point);
447 self.send_touch_event(render_api, event, id);
448 }
449
450 fn on_touch_move(&mut self, render_api: &RenderApi, mut event: TouchEvent, id: InputEventId) {
451 let point = event
452 .point
453 .as_device_point(self.device_pixels_per_page_pixel());
454 let action = self.touch_handler.on_touch_move(
455 event.touch_id,
456 point,
457 self.device_pixels_per_page_pixel_not_including_pinch_zoom()
458 .get(),
459 );
460 if let Some(action) = action {
461 if self
464 .touch_handler
465 .move_allowed(self.touch_handler.current_sequence_id)
466 {
467 event.disable_cancelable();
469 self.pending_scroll_zoom_events.push(action);
470 }
471 }
472 if !self
476 .touch_handler
477 .is_handling_touch_move(self.touch_handler.current_sequence_id) &&
478 self.send_touch_event(render_api, event, id) &&
479 event.is_cancelable()
480 {
481 self.touch_handler
482 .set_handling_touch_move(self.touch_handler.current_sequence_id, true);
483 }
484 }
485
486 fn on_touch_up(&mut self, render_api: &RenderApi, event: TouchEvent, id: InputEventId) {
487 let point = event
488 .point
489 .as_device_point(self.device_pixels_per_page_pixel());
490 self.touch_handler.on_touch_up(event.touch_id, point);
491 self.send_touch_event(render_api, event, id);
492 }
493
494 fn on_touch_cancel(&mut self, render_api: &RenderApi, event: TouchEvent, id: InputEventId) {
495 let point = event
496 .point
497 .as_device_point(self.device_pixels_per_page_pixel());
498 self.touch_handler.on_touch_cancel(event.touch_id, point);
499 self.send_touch_event(render_api, event, id);
500 }
501
502 fn on_touch_event_processed(
503 &mut self,
504 render_api: &RenderApi,
505 pending_touch_input_event: PendingTouchInputEvent,
506 result: InputEventResult,
507 ) {
508 let PendingTouchInputEvent {
510 sequence_id,
511 event_type,
512 touch_id: _,
513 } = pending_touch_input_event;
514
515 if result.contains(InputEventResult::DefaultPrevented) {
516 debug!(
517 "Touch event {:?} in sequence {:?} prevented!",
518 event_type, sequence_id
519 );
520 match event_type {
521 TouchEventType::Down => {
522 self.touch_handler.prevent_click(sequence_id);
524 self.touch_handler.prevent_move(sequence_id);
525 self.touch_handler
526 .remove_pending_touch_move_actions(sequence_id);
527 },
528 TouchEventType::Move => {
529 if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) {
531 info.prevent_move = TouchMoveAllowed::Prevented;
532 if let TouchSequenceState::PendingFling { .. } = info.state {
533 info.state = TouchSequenceState::Finished;
534 }
535 self.touch_handler
536 .set_handling_touch_move(self.touch_handler.current_sequence_id, false);
537 self.touch_handler
538 .remove_pending_touch_move_actions(sequence_id);
539 }
540 },
541 TouchEventType::Up => {
542 let Some(info) = &mut self.touch_handler.get_touch_sequence_mut(sequence_id)
546 else {
547 return;
552 };
553 match info.state {
554 TouchSequenceState::PendingClick(_) => {
555 info.state = TouchSequenceState::Finished;
556 self.touch_handler.remove_touch_sequence(sequence_id);
557 },
558 TouchSequenceState::Flinging { .. } => {
559 },
561 TouchSequenceState::Finished => {
562 self.touch_handler.remove_touch_sequence(sequence_id);
563 },
564 TouchSequenceState::Touching |
565 TouchSequenceState::Panning { .. } |
566 TouchSequenceState::Pinching |
567 TouchSequenceState::MultiTouch |
568 TouchSequenceState::PendingFling { .. } => {
569 },
574 }
575 },
576 TouchEventType::Cancel => {
577 self.touch_handler
580 .remove_pending_touch_move_actions(sequence_id);
581 self.touch_handler.try_remove_touch_sequence(sequence_id);
582 },
583 }
584 } else {
585 debug!(
586 "Touch event {:?} in sequence {:?} allowed",
587 event_type, sequence_id
588 );
589 match event_type {
590 TouchEventType::Down => {},
591 TouchEventType::Move => {
592 self.pending_scroll_zoom_events.extend(
593 self.touch_handler
594 .take_pending_touch_move_actions(sequence_id),
595 );
596 self.touch_handler
597 .set_handling_touch_move(self.touch_handler.current_sequence_id, false);
598 if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) {
599 if info.prevent_move == TouchMoveAllowed::Pending {
600 info.prevent_move = TouchMoveAllowed::Allowed;
601 if let TouchSequenceState::PendingFling { velocity, point } = info.state
602 {
603 info.state = TouchSequenceState::Flinging { velocity, point }
604 }
605 }
606 }
607 },
608 TouchEventType::Up => {
609 let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) else {
610 return;
612 };
613 match info.state {
614 TouchSequenceState::PendingClick(point) => {
615 info.state = TouchSequenceState::Finished;
616 if !info.prevent_click {
619 self.simulate_mouse_click(render_api, point);
620 }
621 self.touch_handler.remove_touch_sequence(sequence_id);
622 },
623 TouchSequenceState::Flinging { .. } => {
624 },
626 TouchSequenceState::Finished => {
627 self.touch_handler.remove_touch_sequence(sequence_id);
628 },
629 TouchSequenceState::Panning { .. } |
630 TouchSequenceState::Pinching |
631 TouchSequenceState::PendingFling { .. } => {
632 },
637 TouchSequenceState::MultiTouch | TouchSequenceState::Touching => {
638 },
640 }
641 },
642 TouchEventType::Cancel => {
643 self.touch_handler
644 .remove_pending_touch_move_actions(sequence_id);
645 self.touch_handler.try_remove_touch_sequence(sequence_id);
646 },
647 }
648 }
649 }
650
651 fn simulate_mouse_click(&mut self, render_api: &RenderApi, point: DevicePoint) {
653 let button = MouseButton::Left;
654 self.dispatch_input_event_with_hit_testing(
655 render_api,
656 InputEvent::MouseMove(MouseMoveEvent::new_compatibility_for_touch(point.into())).into(),
657 );
658 self.dispatch_input_event_with_hit_testing(
659 render_api,
660 InputEvent::MouseButton(MouseButtonEvent::new(
661 MouseButtonAction::Down,
662 button,
663 point.into(),
664 ))
665 .into(),
666 );
667 self.dispatch_input_event_with_hit_testing(
668 render_api,
669 InputEvent::MouseButton(MouseButtonEvent::new(
670 MouseButtonAction::Up,
671 button,
672 point.into(),
673 ))
674 .into(),
675 );
676 }
677
678 pub(crate) fn notify_scroll_event(&mut self, scroll: Scroll, point: WebViewPoint) {
679 let point = point.as_device_point(self.device_pixels_per_page_pixel());
680 self.on_scroll_window_event(scroll, point);
681 }
682
683 fn on_scroll_window_event(&mut self, scroll: Scroll, cursor: DevicePoint) {
684 self.pending_scroll_zoom_events
685 .push(ScrollZoomEvent::Scroll(ScrollEvent {
686 scroll,
687 point: cursor,
688 event_count: 1,
689 }));
690 }
691
692 pub(crate) fn process_pending_scroll_and_pinch_zoom_events(
699 &mut self,
700 render_api: &RenderApi,
701 ) -> (PinchZoomResult, Option<ScrollResult>) {
702 if self.pending_scroll_zoom_events.is_empty() {
703 return (PinchZoomResult::DidNotPinchZoom, None);
704 }
705
706 let mut combined_scroll_event: Option<ScrollEvent> = None;
709 let mut new_pinch_zoom = self.pinch_zoom;
710 let device_pixels_per_page_pixel = self.device_pixels_per_page_pixel();
711
712 for scroll_event in self.pending_scroll_zoom_events.drain(..) {
713 match scroll_event {
714 ScrollZoomEvent::PinchZoom(factor, center) => {
715 new_pinch_zoom.zoom(factor, center);
716 },
717 ScrollZoomEvent::Scroll(scroll_event_info) => {
718 let combined_event = match combined_scroll_event.as_mut() {
719 None => {
720 combined_scroll_event = Some(scroll_event_info);
721 continue;
722 },
723 Some(combined_event) => combined_event,
724 };
725
726 match (combined_event.scroll, scroll_event_info.scroll) {
727 (Scroll::Delta(old_delta), Scroll::Delta(new_delta)) => {
728 let old_event_count = combined_event.event_count as f32;
733 combined_event.event_count += 1;
734 let new_event_count = combined_event.event_count as f32;
735 let old_delta =
736 old_delta.as_device_vector(device_pixels_per_page_pixel);
737 let new_delta =
738 new_delta.as_device_vector(device_pixels_per_page_pixel);
739 let delta = (old_delta * old_event_count + new_delta) / new_event_count;
740 combined_event.scroll = Scroll::Delta(delta.into());
741 },
742 (Scroll::Start, _) | (Scroll::End, _) => {
743 break;
745 },
746 (_, Scroll::Start) | (_, Scroll::End) => {
747 *combined_event = scroll_event_info;
750 break;
751 },
752 }
753 },
754 }
755 }
756
757 if let Some(combined_scroll_event) = combined_scroll_event.as_mut() {
761 new_pinch_zoom.pan(
762 &mut combined_scroll_event.scroll,
763 self.device_pixels_per_page_pixel(),
764 )
765 }
766
767 let scroll_result = combined_scroll_event.and_then(|combined_event| {
768 self.scroll_node_at_device_point(
769 render_api,
770 combined_event.point.to_f32(),
771 combined_event.scroll,
772 )
773 });
774 if let Some(ref scroll_result) = scroll_result {
775 self.send_scroll_positions_to_layout_for_pipeline(
776 scroll_result.hit_test_result.pipeline_id,
777 );
778 self.dispatch_scroll_event(
779 scroll_result.external_scroll_id,
780 scroll_result.hit_test_result.clone(),
781 );
782 } else {
783 self.touch_handler.stop_fling_if_needed();
784 }
785
786 let pinch_zoom_result = self.set_pinch_zoom(new_pinch_zoom);
788 if pinch_zoom_result == PinchZoomResult::DidPinchZoom {
789 self.send_pinch_zoom_infos_to_script();
790 }
791
792 (pinch_zoom_result, scroll_result)
793 }
794
795 fn scroll_node_at_device_point(
800 &mut self,
801 render_api: &RenderApi,
802 cursor: DevicePoint,
803 scroll: Scroll,
804 ) -> Option<ScrollResult> {
805 let scroll_location = match scroll {
806 Scroll::Delta(delta) => {
807 let device_pixels_per_page = self.device_pixels_per_page_pixel();
808 let calculate_delta =
809 delta.as_device_vector(device_pixels_per_page) / device_pixels_per_page;
810 ScrollLocation::Delta(calculate_delta.cast_unit())
811 },
812 Scroll::Start => ScrollLocation::Start,
813 Scroll::End => ScrollLocation::End,
814 };
815
816 let hit_test_results: Vec<_> = self
817 .touch_handler
818 .get_hit_test_result_cache_value()
819 .map(|result| vec![result])
820 .unwrap_or_else(|| self.hit_test(render_api, cursor));
821
822 let mut previous_pipeline_id = None;
826 for hit_test_result in hit_test_results {
827 let pipeline_details = self.pipelines.get_mut(&hit_test_result.pipeline_id)?;
828 if previous_pipeline_id.replace(hit_test_result.pipeline_id) !=
829 Some(hit_test_result.pipeline_id)
830 {
831 let scroll_result = pipeline_details.scroll_tree.scroll_node_or_ancestor(
832 hit_test_result.external_scroll_id,
833 scroll_location,
834 ScrollType::InputEvents,
835 );
836 if let Some((external_scroll_id, offset)) = scroll_result {
837 self.touch_handler.set_hit_test_result_cache_value(
843 hit_test_result.clone(),
844 self.device_pixels_per_page_pixel(),
845 );
846 return Some(ScrollResult {
847 hit_test_result,
848 external_scroll_id,
849 offset,
850 });
851 }
852 }
853 }
854 None
855 }
856
857 pub(crate) fn scroll_viewport_by_delta(
861 &mut self,
862 delta: LayoutVector2D,
863 ) -> (PinchZoomResult, Vec<ScrollResult>) {
864 let device_pixels_per_page_pixel = self.device_pixels_per_page_pixel();
865 let delta_in_device_pixels = delta.cast_unit() * device_pixels_per_page_pixel;
866 let remaining = self.pinch_zoom.pan_with_device_scroll(
867 Scroll::Delta(delta_in_device_pixels.into()),
868 device_pixels_per_page_pixel,
869 );
870
871 let pinch_zoom_result = match remaining == delta_in_device_pixels {
872 true => PinchZoomResult::DidNotPinchZoom,
873 false => PinchZoomResult::DidPinchZoom,
874 };
875 if remaining == Vector2D::zero() {
876 return (pinch_zoom_result, vec![]);
877 }
878
879 let Some(root_pipeline_id) = self.root_pipeline_id else {
880 return (pinch_zoom_result, vec![]);
881 };
882 let Some(root_pipeline) = self.pipelines.get_mut(&root_pipeline_id) else {
883 return (pinch_zoom_result, vec![]);
884 };
885
886 let remaining = remaining / device_pixels_per_page_pixel;
887 let Some((external_scroll_id, offset)) = root_pipeline.scroll_tree.scroll_node_or_ancestor(
888 ExternalScrollId(0, root_pipeline_id.into()),
889 ScrollLocation::Delta(remaining.cast_unit()),
890 ScrollType::InputEvents,
892 ) else {
893 return (pinch_zoom_result, vec![]);
894 };
895
896 let hit_test_result = PaintHitTestResult {
897 pipeline_id: root_pipeline_id,
898 point_in_viewport: Default::default(),
901 external_scroll_id,
902 };
903
904 self.send_scroll_positions_to_layout_for_pipeline(root_pipeline_id);
905 self.dispatch_scroll_event(external_scroll_id, hit_test_result.clone());
906
907 if pinch_zoom_result == PinchZoomResult::DidNotPinchZoom {
908 self.send_pinch_zoom_infos_to_script();
909 }
910
911 let scroll_result = ScrollResult {
912 hit_test_result,
913 external_scroll_id,
914 offset,
915 };
916 (pinch_zoom_result, vec![scroll_result])
917 }
918
919 fn send_pinch_zoom_infos_to_script(&self) {
921 let Some(pipeline_id) = self.root_pipeline_id else {
923 return;
924 };
925
926 let pinch_zoom_infos = self.pinch_zoom.get_pinch_zoom_infos_for_script(
927 self.device_pixels_per_page_pixel_not_including_pinch_zoom(),
928 );
929
930 let _ = self.embedder_to_constellation_sender.send(
931 EmbedderToConstellationMessage::UpdatePinchZoomInfos(pipeline_id, pinch_zoom_infos),
932 );
933 }
934
935 fn dispatch_scroll_event(
936 &self,
937 external_id: ExternalScrollId,
938 hit_test_result: PaintHitTestResult,
939 ) {
940 let event = InputEvent::Scroll(EmbedderScrollEvent { external_id }).into();
941 let msg = EmbedderToConstellationMessage::ForwardInputEvent(
942 self.id,
943 event,
944 Some(hit_test_result),
945 );
946 if let Err(e) = self.embedder_to_constellation_sender.send(msg) {
947 warn!("Sending scroll event to constellation failed ({:?}).", e);
948 }
949 }
950
951 pub(crate) fn pinch_zoom(&self) -> PinchZoom {
952 self.pinch_zoom
953 }
954
955 fn set_pinch_zoom(&mut self, requested_pinch_zoom: PinchZoom) -> PinchZoomResult {
956 if requested_pinch_zoom == self.pinch_zoom {
957 return PinchZoomResult::DidNotPinchZoom;
958 }
959
960 self.pinch_zoom = requested_pinch_zoom;
961 PinchZoomResult::DidPinchZoom
962 }
963
964 pub(crate) fn set_page_zoom(
965 &mut self,
966 new_page_zoom: Scale<f32, CSSPixel, DeviceIndependentPixel>,
967 ) {
968 let new_page_zoom = new_page_zoom.clamp(MIN_PAGE_ZOOM, MAX_PAGE_ZOOM);
969 let old_zoom = std::mem::replace(&mut self.page_zoom, new_page_zoom);
970 if old_zoom != self.page_zoom {
971 self.send_window_size_message();
972 }
973 }
974
975 pub(crate) fn device_pixels_per_page_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> {
981 let viewport_scale = self
982 .root_pipeline_id
983 .and_then(|pipeline_id| self.pipelines.get(&pipeline_id))
984 .and_then(|pipeline| pipeline.viewport_scale)
985 .unwrap_or_else(|| self.page_zoom * self.hidpi_scale_factor);
986 viewport_scale * self.pinch_zoom.zoom_factor()
987 }
988
989 pub(crate) fn device_pixels_per_page_pixel_not_including_pinch_zoom(
994 &self,
995 ) -> Scale<f32, CSSPixel, DevicePixel> {
996 self.page_zoom * self.hidpi_scale_factor
997 }
998
999 pub(crate) fn adjust_pinch_zoom(&mut self, magnification: f32, center: DevicePoint) {
1001 if magnification == 1.0 {
1002 return;
1003 }
1004
1005 self.pending_scroll_zoom_events
1006 .push(ScrollZoomEvent::PinchZoom(magnification, center));
1007 }
1008
1009 fn send_window_size_message(&self) {
1010 let device_pixel_ratio = self.device_pixels_per_page_pixel_not_including_pinch_zoom();
1013 let initial_viewport = self.rect.size().to_f32() / device_pixel_ratio;
1014 let _ = self.embedder_to_constellation_sender.send(
1015 EmbedderToConstellationMessage::ChangeViewportDetails(
1016 self.id,
1017 ViewportDetails {
1018 hidpi_scale_factor: device_pixel_ratio,
1019 size: initial_viewport,
1020 },
1021 WindowSizeType::Resize,
1022 ),
1023 );
1024 }
1025
1026 pub(crate) fn set_hidpi_scale_factor(
1028 &mut self,
1029 new_scale: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1030 ) -> bool {
1031 let old_scale_factor = std::mem::replace(&mut self.hidpi_scale_factor, new_scale);
1032 if self.hidpi_scale_factor == old_scale_factor {
1033 return false;
1034 }
1035
1036 self.send_window_size_message();
1037 true
1038 }
1039
1040 pub(crate) fn set_rect(&mut self, new_rect: DeviceRect) -> bool {
1042 let old_rect = std::mem::replace(&mut self.rect, new_rect);
1043 if old_rect.size() != self.rect.size() {
1044 self.send_window_size_message();
1045 self.pinch_zoom.resize_unscaled_viewport(new_rect);
1046 self.send_pinch_zoom_infos_to_script();
1047 }
1048 old_rect != self.rect
1049 }
1050
1051 pub fn set_viewport_description(&mut self, viewport_description: ViewportDescription) {
1052 self.set_page_zoom(Scale::new(
1053 viewport_description.clamp_page_zoom(viewport_description.initial_scale.get()),
1054 ));
1055 self.viewport_description = Some(viewport_description);
1056 }
1057
1058 pub(crate) fn scroll_trees_memory_usage(
1059 &self,
1060 ops: &mut malloc_size_of::MallocSizeOfOps,
1061 ) -> usize {
1062 self.pipelines
1063 .values()
1064 .map(|pipeline| pipeline.scroll_tree.size_of(ops))
1065 .sum::<usize>()
1066 }
1067
1068 pub(crate) fn notify_input_event_handled(
1069 &mut self,
1070 render_api: &RenderApi,
1071 repaint_reason: &Cell<RepaintReason>,
1072 id: InputEventId,
1073 result: InputEventResult,
1074 ) {
1075 if let Some(pending_touch_input_event) =
1076 self.touch_handler.take_pending_touch_input_event(id)
1077 {
1078 self.on_touch_event_processed(render_api, pending_touch_input_event, result);
1079 self.touch_handler
1080 .add_touch_move_refresh_observer_if_necessary(
1081 self.refresh_driver.clone(),
1082 repaint_reason,
1083 );
1084 }
1085
1086 if let Some(wheel_event) = self.pending_wheel_events.remove(&id) {
1087 if !result.contains(InputEventResult::DefaultPrevented) {
1088 let scroll_delta =
1090 DeviceVector2D::new(-wheel_event.delta.x as f32, -wheel_event.delta.y as f32);
1091 self.notify_scroll_event(Scroll::Delta(scroll_delta.into()), wheel_event.point);
1092 }
1093 }
1094 }
1095}
1096
1097#[derive(Clone, Copy, Debug, PartialEq)]
1098pub struct UnknownWebView(pub WebViewId);