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_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.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(event.id, point);
455 if let Some(action) = action {
456 if self
459 .touch_handler
460 .move_allowed(self.touch_handler.current_sequence_id)
461 {
462 event.disable_cancelable();
464 self.pending_scroll_zoom_events.push(action);
465 }
466 if !self
470 .touch_handler
471 .is_handling_touch_move(self.touch_handler.current_sequence_id) &&
472 self.send_touch_event(render_api, event, id) &&
473 event.is_cancelable()
474 {
475 self.touch_handler
476 .set_handling_touch_move(self.touch_handler.current_sequence_id, true);
477 }
478 }
479 }
480
481 fn on_touch_up(&mut self, render_api: &RenderApi, event: TouchEvent, id: InputEventId) {
482 let point = event
483 .point
484 .as_device_point(self.device_pixels_per_page_pixel());
485 self.touch_handler.on_touch_up(event.id, point);
486 self.send_touch_event(render_api, event, id);
487 }
488
489 fn on_touch_cancel(&mut self, render_api: &RenderApi, event: TouchEvent, id: InputEventId) {
490 let point = event
491 .point
492 .as_device_point(self.device_pixels_per_page_pixel());
493 self.touch_handler.on_touch_cancel(event.id, point);
494 self.send_touch_event(render_api, event, id);
495 }
496
497 fn on_touch_event_processed(
498 &mut self,
499 render_api: &RenderApi,
500 pending_touch_input_event: PendingTouchInputEvent,
501 result: InputEventResult,
502 ) {
503 let PendingTouchInputEvent {
504 sequence_id,
505 event_type,
506 } = pending_touch_input_event;
507
508 if result.contains(InputEventResult::DefaultPrevented) {
509 debug!(
510 "Touch event {:?} in sequence {:?} prevented!",
511 event_type, sequence_id
512 );
513 match event_type {
514 TouchEventType::Down => {
515 self.touch_handler.prevent_click(sequence_id);
517 self.touch_handler.prevent_move(sequence_id);
518 self.touch_handler
519 .remove_pending_touch_move_actions(sequence_id);
520 },
521 TouchEventType::Move => {
522 if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) {
524 info.prevent_move = TouchMoveAllowed::Prevented;
525 if let TouchSequenceState::PendingFling { .. } = info.state {
526 info.state = TouchSequenceState::Finished;
527 }
528 self.touch_handler
529 .set_handling_touch_move(self.touch_handler.current_sequence_id, false);
530 self.touch_handler
531 .remove_pending_touch_move_actions(sequence_id);
532 }
533 },
534 TouchEventType::Up => {
535 let Some(info) = &mut self.touch_handler.get_touch_sequence_mut(sequence_id)
539 else {
540 return;
545 };
546 match info.state {
547 TouchSequenceState::PendingClick(_) => {
548 info.state = TouchSequenceState::Finished;
549 self.touch_handler.remove_touch_sequence(sequence_id);
550 },
551 TouchSequenceState::Flinging { .. } => {
552 },
554 TouchSequenceState::Finished => {
555 self.touch_handler.remove_touch_sequence(sequence_id);
556 },
557 TouchSequenceState::Touching |
558 TouchSequenceState::Panning { .. } |
559 TouchSequenceState::Pinching |
560 TouchSequenceState::MultiTouch |
561 TouchSequenceState::PendingFling { .. } => {
562 },
567 }
568 },
569 TouchEventType::Cancel => {
570 self.touch_handler
573 .remove_pending_touch_move_actions(sequence_id);
574 self.touch_handler.try_remove_touch_sequence(sequence_id);
575 },
576 }
577 } else {
578 debug!(
579 "Touch event {:?} in sequence {:?} allowed",
580 event_type, sequence_id
581 );
582 match event_type {
583 TouchEventType::Down => {},
584 TouchEventType::Move => {
585 self.pending_scroll_zoom_events.extend(
586 self.touch_handler
587 .take_pending_touch_move_actions(sequence_id),
588 );
589 self.touch_handler
590 .set_handling_touch_move(self.touch_handler.current_sequence_id, false);
591 if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) {
592 if info.prevent_move == TouchMoveAllowed::Pending {
593 info.prevent_move = TouchMoveAllowed::Allowed;
594 if let TouchSequenceState::PendingFling { velocity, point } = info.state
595 {
596 info.state = TouchSequenceState::Flinging { velocity, point }
597 }
598 }
599 }
600 },
601 TouchEventType::Up => {
602 let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) else {
603 return;
605 };
606 match info.state {
607 TouchSequenceState::PendingClick(point) => {
608 info.state = TouchSequenceState::Finished;
609 if !info.prevent_click {
612 self.simulate_mouse_click(render_api, point);
613 }
614 self.touch_handler.remove_touch_sequence(sequence_id);
615 },
616 TouchSequenceState::Flinging { .. } => {
617 },
619 TouchSequenceState::Finished => {
620 self.touch_handler.remove_touch_sequence(sequence_id);
621 },
622 TouchSequenceState::Panning { .. } |
623 TouchSequenceState::Pinching |
624 TouchSequenceState::PendingFling { .. } => {
625 },
630 TouchSequenceState::MultiTouch | TouchSequenceState::Touching => {
631 },
633 }
634 },
635 TouchEventType::Cancel => {
636 self.touch_handler
637 .remove_pending_touch_move_actions(sequence_id);
638 self.touch_handler.try_remove_touch_sequence(sequence_id);
639 },
640 }
641 }
642 }
643
644 fn simulate_mouse_click(&mut self, render_api: &RenderApi, point: DevicePoint) {
646 let button = MouseButton::Left;
647 self.dispatch_input_event_with_hit_testing(
648 render_api,
649 InputEvent::MouseMove(MouseMoveEvent::new_compatibility_for_touch(point.into())).into(),
650 );
651 self.dispatch_input_event_with_hit_testing(
652 render_api,
653 InputEvent::MouseButton(MouseButtonEvent::new(
654 MouseButtonAction::Down,
655 button,
656 point.into(),
657 ))
658 .into(),
659 );
660 self.dispatch_input_event_with_hit_testing(
661 render_api,
662 InputEvent::MouseButton(MouseButtonEvent::new(
663 MouseButtonAction::Up,
664 button,
665 point.into(),
666 ))
667 .into(),
668 );
669 }
670
671 pub(crate) fn notify_scroll_event(&mut self, scroll: Scroll, point: WebViewPoint) {
672 let point = point.as_device_point(self.device_pixels_per_page_pixel());
673 self.on_scroll_window_event(scroll, point);
674 }
675
676 fn on_scroll_window_event(&mut self, scroll: Scroll, cursor: DevicePoint) {
677 self.pending_scroll_zoom_events
678 .push(ScrollZoomEvent::Scroll(ScrollEvent {
679 scroll,
680 point: cursor,
681 event_count: 1,
682 }));
683 }
684
685 pub(crate) fn process_pending_scroll_and_pinch_zoom_events(
692 &mut self,
693 render_api: &RenderApi,
694 ) -> (PinchZoomResult, Option<ScrollResult>) {
695 if self.pending_scroll_zoom_events.is_empty() {
696 return (PinchZoomResult::DidNotPinchZoom, None);
697 }
698
699 let mut combined_scroll_event: Option<ScrollEvent> = None;
702 let mut new_pinch_zoom = self.pinch_zoom;
703 let device_pixels_per_page_pixel = self.device_pixels_per_page_pixel();
704
705 for scroll_event in self.pending_scroll_zoom_events.drain(..) {
706 match scroll_event {
707 ScrollZoomEvent::PinchZoom(factor, center) => {
708 new_pinch_zoom.zoom(factor, center);
709 },
710 ScrollZoomEvent::Scroll(scroll_event_info) => {
711 let combined_event = match combined_scroll_event.as_mut() {
712 None => {
713 combined_scroll_event = Some(scroll_event_info);
714 continue;
715 },
716 Some(combined_event) => combined_event,
717 };
718
719 match (combined_event.scroll, scroll_event_info.scroll) {
720 (Scroll::Delta(old_delta), Scroll::Delta(new_delta)) => {
721 let old_event_count = combined_event.event_count as f32;
726 combined_event.event_count += 1;
727 let new_event_count = combined_event.event_count as f32;
728 let old_delta =
729 old_delta.as_device_vector(device_pixels_per_page_pixel);
730 let new_delta =
731 new_delta.as_device_vector(device_pixels_per_page_pixel);
732 let delta = (old_delta * old_event_count + new_delta) / new_event_count;
733 combined_event.scroll = Scroll::Delta(delta.into());
734 },
735 (Scroll::Start, _) | (Scroll::End, _) => {
736 break;
738 },
739 (_, Scroll::Start) | (_, Scroll::End) => {
740 *combined_event = scroll_event_info;
743 break;
744 },
745 }
746 },
747 }
748 }
749
750 if let Some(combined_scroll_event) = combined_scroll_event.as_mut() {
754 new_pinch_zoom.pan(
755 &mut combined_scroll_event.scroll,
756 self.device_pixels_per_page_pixel(),
757 )
758 }
759
760 let scroll_result = combined_scroll_event.and_then(|combined_event| {
761 self.scroll_node_at_device_point(
762 render_api,
763 combined_event.point.to_f32(),
764 combined_event.scroll,
765 )
766 });
767 if let Some(ref scroll_result) = scroll_result {
768 self.send_scroll_positions_to_layout_for_pipeline(
769 scroll_result.hit_test_result.pipeline_id,
770 );
771 self.dispatch_scroll_event(
772 scroll_result.external_scroll_id,
773 scroll_result.hit_test_result.clone(),
774 );
775 } else {
776 self.touch_handler.stop_fling_if_needed();
777 }
778
779 let pinch_zoom_result = self.set_pinch_zoom(new_pinch_zoom);
781 if pinch_zoom_result == PinchZoomResult::DidPinchZoom {
782 self.send_pinch_zoom_infos_to_script();
783 }
784
785 (pinch_zoom_result, scroll_result)
786 }
787
788 fn scroll_node_at_device_point(
793 &mut self,
794 render_api: &RenderApi,
795 cursor: DevicePoint,
796 scroll: Scroll,
797 ) -> Option<ScrollResult> {
798 let scroll_location = match scroll {
799 Scroll::Delta(delta) => {
800 let device_pixels_per_page = self.device_pixels_per_page_pixel();
801 let calculate_delta =
802 delta.as_device_vector(device_pixels_per_page) / device_pixels_per_page;
803 ScrollLocation::Delta(calculate_delta.cast_unit())
804 },
805 Scroll::Start => ScrollLocation::Start,
806 Scroll::End => ScrollLocation::End,
807 };
808
809 let hit_test_results: Vec<_> = self
810 .touch_handler
811 .get_hit_test_result_cache_value()
812 .map(|result| vec![result])
813 .unwrap_or_else(|| self.hit_test(render_api, cursor));
814
815 let mut previous_pipeline_id = None;
819 for hit_test_result in hit_test_results {
820 let pipeline_details = self.pipelines.get_mut(&hit_test_result.pipeline_id)?;
821 if previous_pipeline_id.replace(hit_test_result.pipeline_id) !=
822 Some(hit_test_result.pipeline_id)
823 {
824 let scroll_result = pipeline_details.scroll_tree.scroll_node_or_ancestor(
825 hit_test_result.external_scroll_id,
826 scroll_location,
827 ScrollType::InputEvents,
828 );
829 if let Some((external_scroll_id, offset)) = scroll_result {
830 self.touch_handler.set_hit_test_result_cache_value(
836 hit_test_result.clone(),
837 self.device_pixels_per_page_pixel(),
838 );
839 return Some(ScrollResult {
840 hit_test_result,
841 external_scroll_id,
842 offset,
843 });
844 }
845 }
846 }
847 None
848 }
849
850 pub(crate) fn scroll_viewport_by_delta(
854 &mut self,
855 delta: LayoutVector2D,
856 ) -> (PinchZoomResult, Vec<ScrollResult>) {
857 let device_pixels_per_page_pixel = self.device_pixels_per_page_pixel();
858 let delta_in_device_pixels = delta.cast_unit() * device_pixels_per_page_pixel;
859 let remaining = self.pinch_zoom.pan_with_device_scroll(
860 Scroll::Delta(delta_in_device_pixels.into()),
861 device_pixels_per_page_pixel,
862 );
863
864 let pinch_zoom_result = match remaining == delta_in_device_pixels {
865 true => PinchZoomResult::DidNotPinchZoom,
866 false => PinchZoomResult::DidPinchZoom,
867 };
868 if remaining == Vector2D::zero() {
869 return (pinch_zoom_result, vec![]);
870 }
871
872 let Some(root_pipeline_id) = self.root_pipeline_id else {
873 return (pinch_zoom_result, vec![]);
874 };
875 let Some(root_pipeline) = self.pipelines.get_mut(&root_pipeline_id) else {
876 return (pinch_zoom_result, vec![]);
877 };
878
879 let remaining = remaining / device_pixels_per_page_pixel;
880 let Some((external_scroll_id, offset)) = root_pipeline.scroll_tree.scroll_node_or_ancestor(
881 ExternalScrollId(0, root_pipeline_id.into()),
882 ScrollLocation::Delta(remaining.cast_unit()),
883 ScrollType::InputEvents,
885 ) else {
886 return (pinch_zoom_result, vec![]);
887 };
888
889 let hit_test_result = PaintHitTestResult {
890 pipeline_id: root_pipeline_id,
891 point_in_viewport: Default::default(),
894 external_scroll_id,
895 };
896
897 self.send_scroll_positions_to_layout_for_pipeline(root_pipeline_id);
898 self.dispatch_scroll_event(external_scroll_id, hit_test_result.clone());
899
900 if pinch_zoom_result == PinchZoomResult::DidNotPinchZoom {
901 self.send_pinch_zoom_infos_to_script();
902 }
903
904 let scroll_result = ScrollResult {
905 hit_test_result,
906 external_scroll_id,
907 offset,
908 };
909 (pinch_zoom_result, vec![scroll_result])
910 }
911
912 fn send_pinch_zoom_infos_to_script(&self) {
914 let Some(pipeline_id) = self.root_pipeline_id else {
916 return;
917 };
918
919 let pinch_zoom_infos = self.pinch_zoom.get_pinch_zoom_infos_for_script(
920 self.device_pixels_per_page_pixel_not_including_pinch_zoom(),
921 );
922
923 let _ = self.embedder_to_constellation_sender.send(
924 EmbedderToConstellationMessage::UpdatePinchZoomInfos(pipeline_id, pinch_zoom_infos),
925 );
926 }
927
928 fn dispatch_scroll_event(
929 &self,
930 external_id: ExternalScrollId,
931 hit_test_result: PaintHitTestResult,
932 ) {
933 let event = InputEvent::Scroll(EmbedderScrollEvent { external_id }).into();
934 let msg = EmbedderToConstellationMessage::ForwardInputEvent(
935 self.id,
936 event,
937 Some(hit_test_result),
938 );
939 if let Err(e) = self.embedder_to_constellation_sender.send(msg) {
940 warn!("Sending scroll event to constellation failed ({:?}).", e);
941 }
942 }
943
944 pub(crate) fn pinch_zoom(&self) -> PinchZoom {
945 self.pinch_zoom
946 }
947
948 fn set_pinch_zoom(&mut self, requested_pinch_zoom: PinchZoom) -> PinchZoomResult {
949 if requested_pinch_zoom == self.pinch_zoom {
950 return PinchZoomResult::DidNotPinchZoom;
951 }
952
953 self.pinch_zoom = requested_pinch_zoom;
954 PinchZoomResult::DidPinchZoom
955 }
956
957 pub(crate) fn set_page_zoom(
958 &mut self,
959 new_page_zoom: Scale<f32, CSSPixel, DeviceIndependentPixel>,
960 ) {
961 let new_page_zoom = new_page_zoom.clamp(MIN_PAGE_ZOOM, MAX_PAGE_ZOOM);
962 let old_zoom = std::mem::replace(&mut self.page_zoom, new_page_zoom);
963 if old_zoom != self.page_zoom {
964 self.send_window_size_message();
965 }
966 }
967
968 pub(crate) fn device_pixels_per_page_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> {
974 let viewport_scale = self
975 .root_pipeline_id
976 .and_then(|pipeline_id| self.pipelines.get(&pipeline_id))
977 .and_then(|pipeline| pipeline.viewport_scale)
978 .unwrap_or_else(|| self.page_zoom * self.hidpi_scale_factor);
979 viewport_scale * self.pinch_zoom.zoom_factor()
980 }
981
982 pub(crate) fn device_pixels_per_page_pixel_not_including_pinch_zoom(
987 &self,
988 ) -> Scale<f32, CSSPixel, DevicePixel> {
989 self.page_zoom * self.hidpi_scale_factor
990 }
991
992 pub(crate) fn adjust_pinch_zoom(&mut self, magnification: f32, center: DevicePoint) {
994 if magnification == 1.0 {
995 return;
996 }
997
998 self.pending_scroll_zoom_events
999 .push(ScrollZoomEvent::PinchZoom(magnification, center));
1000 }
1001
1002 fn send_window_size_message(&self) {
1003 let device_pixel_ratio = self.device_pixels_per_page_pixel_not_including_pinch_zoom();
1006 let initial_viewport = self.rect.size().to_f32() / device_pixel_ratio;
1007 let _ = self.embedder_to_constellation_sender.send(
1008 EmbedderToConstellationMessage::ChangeViewportDetails(
1009 self.id,
1010 ViewportDetails {
1011 hidpi_scale_factor: device_pixel_ratio,
1012 size: initial_viewport,
1013 },
1014 WindowSizeType::Resize,
1015 ),
1016 );
1017 }
1018
1019 pub(crate) fn set_hidpi_scale_factor(
1021 &mut self,
1022 new_scale: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1023 ) -> bool {
1024 let old_scale_factor = std::mem::replace(&mut self.hidpi_scale_factor, new_scale);
1025 if self.hidpi_scale_factor == old_scale_factor {
1026 return false;
1027 }
1028
1029 self.send_window_size_message();
1030 true
1031 }
1032
1033 pub(crate) fn set_rect(&mut self, new_rect: DeviceRect) -> bool {
1035 let old_rect = std::mem::replace(&mut self.rect, new_rect);
1036 if old_rect.size() != self.rect.size() {
1037 self.send_window_size_message();
1038 self.pinch_zoom.resize_unscaled_viewport(new_rect);
1039 self.send_pinch_zoom_infos_to_script();
1040 }
1041 old_rect != self.rect
1042 }
1043
1044 pub fn set_viewport_description(&mut self, viewport_description: ViewportDescription) {
1045 self.set_page_zoom(Scale::new(
1046 viewport_description.clamp_page_zoom(viewport_description.initial_scale.get()),
1047 ));
1048 self.viewport_description = Some(viewport_description);
1049 }
1050
1051 pub(crate) fn scroll_trees_memory_usage(
1052 &self,
1053 ops: &mut malloc_size_of::MallocSizeOfOps,
1054 ) -> usize {
1055 self.pipelines
1056 .values()
1057 .map(|pipeline| pipeline.scroll_tree.size_of(ops))
1058 .sum::<usize>()
1059 }
1060
1061 pub(crate) fn notify_input_event_handled(
1062 &mut self,
1063 render_api: &RenderApi,
1064 repaint_reason: &Cell<RepaintReason>,
1065 id: InputEventId,
1066 result: InputEventResult,
1067 ) {
1068 if let Some(pending_touch_input_event) =
1069 self.touch_handler.take_pending_touch_input_event(id)
1070 {
1071 self.on_touch_event_processed(render_api, pending_touch_input_event, result);
1072 self.touch_handler
1073 .add_touch_move_refresh_observer_if_necessary(
1074 self.refresh_driver.clone(),
1075 repaint_reason,
1076 );
1077 }
1078
1079 if let Some(wheel_event) = self.pending_wheel_events.remove(&id) {
1080 if !result.contains(InputEventResult::DefaultPrevented) {
1081 let scroll_delta =
1083 DeviceVector2D::new(-wheel_event.delta.x as f32, -wheel_event.delta.y as f32);
1084 self.notify_scroll_event(Scroll::Delta(scroll_delta.into()), wheel_event.point);
1085 }
1086 }
1087 }
1088}
1089
1090#[derive(Clone, Copy, Debug, PartialEq)]
1091pub struct UnknownWebView(pub WebViewId);