1use std::cell::RefCell;
6use std::collections::hash_map::Entry;
7use std::rc::Rc;
8
9use base::id::{PipelineId, WebViewId};
10use compositing_traits::display_list::ScrollType;
11use compositing_traits::viewport_description::{
12 DEFAULT_PAGE_ZOOM, MAX_PAGE_ZOOM, MIN_PAGE_ZOOM, ViewportDescription,
13};
14use compositing_traits::{PipelineExitSource, SendableFrameTree, WebViewTrait};
15use constellation_traits::{EmbedderToConstellationMessage, WindowSizeType};
16use embedder_traits::{
17 AnimationState, CompositorHitTestResult, InputEvent, InputEventAndId, MouseButton,
18 MouseButtonAction, MouseButtonEvent, MouseMoveEvent, ScrollEvent as EmbedderScrollEvent,
19 ShutdownState, TouchEvent, TouchEventResult, TouchEventType, ViewportDetails,
20};
21use euclid::{Point2D, Scale, Vector2D};
22use log::{debug, warn};
23use malloc_size_of::MallocSizeOf;
24use rustc_hash::FxHashMap;
25use servo_geometry::DeviceIndependentPixel;
26use style_traits::{CSSPixel, PinchZoomFactor};
27use webrender_api::units::{DeviceIntPoint, DevicePixel, DevicePoint, DeviceRect, LayoutVector2D};
28use webrender_api::{ExternalScrollId, ScrollLocation};
29
30use crate::compositor::{PipelineDetails, ServoRenderer};
31use crate::touch::{TouchHandler, TouchMoveAction, TouchMoveAllowed, TouchSequenceState};
32
33#[derive(Clone, Copy)]
34struct ScrollEvent {
35 scroll_location: ScrollLocation,
37 cursor: DeviceIntPoint,
39 event_count: u32,
41}
42
43#[derive(Clone, Copy)]
44enum ScrollZoomEvent {
45 PinchZoom(f32),
47 InitialViewportZoom(f32),
49 Scroll(ScrollEvent),
52}
53
54#[derive(Clone, Debug)]
55pub(crate) struct ScrollResult {
56 pub hit_test_result: CompositorHitTestResult,
57 pub external_scroll_id: ExternalScrollId,
58 pub offset: LayoutVector2D,
59}
60
61#[derive(Debug, PartialEq)]
62pub(crate) enum PinchZoomResult {
63 DidPinchZoom,
64 DidNotPinchZoom,
65}
66
67pub(crate) struct WebViewRenderer {
71 pub id: WebViewId,
73 pub webview: Box<dyn WebViewTrait>,
77 pub root_pipeline_id: Option<PipelineId>,
79 pub rect: DeviceRect,
81 pub pipelines: FxHashMap<PipelineId, PipelineDetails>,
83 pub(crate) global: Rc<RefCell<ServoRenderer>>,
85 pending_scroll_zoom_events: Vec<ScrollZoomEvent>,
87 touch_handler: TouchHandler,
89 pub page_zoom: Scale<f32, CSSPixel, DeviceIndependentPixel>,
91 pinch_zoom: PinchZoomFactor,
93 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
96 animating: bool,
99 viewport_description: Option<ViewportDescription>,
102}
103
104impl WebViewRenderer {
105 pub(crate) fn new(
106 global: Rc<RefCell<ServoRenderer>>,
107 renderer_webview: Box<dyn WebViewTrait>,
108 viewport_details: ViewportDetails,
109 ) -> Self {
110 let hidpi_scale_factor = viewport_details.hidpi_scale_factor;
111 let size = viewport_details.size * viewport_details.hidpi_scale_factor;
112 Self {
113 id: renderer_webview.id(),
114 webview: renderer_webview,
115 root_pipeline_id: None,
116 rect: DeviceRect::from_origin_and_size(DevicePoint::origin(), size),
117 pipelines: Default::default(),
118 touch_handler: TouchHandler::new(),
119 global,
120 pending_scroll_zoom_events: Default::default(),
121 page_zoom: DEFAULT_PAGE_ZOOM,
122 pinch_zoom: PinchZoomFactor::new(1.0),
123 hidpi_scale_factor: Scale::new(hidpi_scale_factor.0),
124 animating: false,
125 viewport_description: None,
126 }
127 }
128
129 pub(crate) fn animation_callbacks_running(&self) -> bool {
130 self.pipelines
131 .values()
132 .any(PipelineDetails::animation_callbacks_running)
133 }
134
135 pub(crate) fn animating(&self) -> bool {
136 self.animating
137 }
138
139 pub(crate) fn ensure_pipeline_details(
141 &mut self,
142 pipeline_id: PipelineId,
143 ) -> &mut PipelineDetails {
144 self.pipelines
145 .entry(pipeline_id)
146 .or_insert_with(PipelineDetails::new)
147 }
148
149 pub(crate) fn pipeline_exited(&mut self, pipeline_id: PipelineId, source: PipelineExitSource) {
150 let pipeline = self.pipelines.entry(pipeline_id);
151 let Entry::Occupied(mut pipeline) = pipeline else {
152 return;
153 };
154
155 pipeline.get_mut().exited.insert(source);
156
157 if !pipeline.get().exited.is_all() {
161 return;
162 }
163
164 pipeline.remove_entry();
165 }
166
167 pub(crate) fn set_frame_tree(&mut self, frame_tree: &SendableFrameTree) {
168 let pipeline_id = frame_tree.pipeline.id;
169 let old_pipeline_id = self.root_pipeline_id.replace(pipeline_id);
170
171 if old_pipeline_id != self.root_pipeline_id {
172 debug!(
173 "Updating webview ({:?}) from pipeline {:?} to {:?}",
174 3, old_pipeline_id, self.root_pipeline_id
175 );
176 }
177
178 self.set_frame_tree_on_pipeline_details(frame_tree, None);
179 }
180
181 pub(crate) fn send_scroll_positions_to_layout_for_pipeline(&self, pipeline_id: PipelineId) {
182 let Some(details) = self.pipelines.get(&pipeline_id) else {
183 return;
184 };
185
186 let scroll_offsets = details.scroll_tree.scroll_offsets();
187
188 if scroll_offsets.is_empty() {
193 return;
194 }
195
196 let _ = self.global.borrow().constellation_sender.send(
197 EmbedderToConstellationMessage::SetScrollStates(pipeline_id, scroll_offsets),
198 );
199 }
200
201 pub(crate) fn set_frame_tree_on_pipeline_details(
202 &mut self,
203 frame_tree: &SendableFrameTree,
204 parent_pipeline_id: Option<PipelineId>,
205 ) {
206 let pipeline_id = frame_tree.pipeline.id;
207 let pipeline_details = self.ensure_pipeline_details(pipeline_id);
208 pipeline_details.pipeline = Some(frame_tree.pipeline.clone());
209 pipeline_details.parent_pipeline_id = parent_pipeline_id;
210
211 for kid in &frame_tree.children {
212 self.set_frame_tree_on_pipeline_details(kid, Some(pipeline_id));
213 }
214 }
215
216 pub(crate) fn change_pipeline_running_animations_state(
219 &mut self,
220 pipeline_id: PipelineId,
221 animation_state: AnimationState,
222 ) -> bool {
223 let pipeline_details = self.ensure_pipeline_details(pipeline_id);
224 let was_animating = pipeline_details.animating();
225 match animation_state {
226 AnimationState::AnimationsPresent => {
227 pipeline_details.animations_running = true;
228 },
229 AnimationState::AnimationCallbacksPresent => {
230 pipeline_details.animation_callbacks_running = true;
231 },
232 AnimationState::NoAnimationsPresent => {
233 pipeline_details.animations_running = false;
234 },
235 AnimationState::NoAnimationCallbacksPresent => {
236 pipeline_details.animation_callbacks_running = false;
237 },
238 }
239 let started_animating = !was_animating && pipeline_details.animating();
240
241 self.update_animation_state();
242
243 started_animating
248 }
249
250 pub(crate) fn set_throttled(&mut self, pipeline_id: PipelineId, throttled: bool) -> bool {
253 let pipeline_details = self.ensure_pipeline_details(pipeline_id);
254 let was_animating = pipeline_details.animating();
255 pipeline_details.throttled = throttled;
256 let started_animating = !was_animating && pipeline_details.animating();
257
258 self.update_animation_state();
260
261 started_animating
266 }
267
268 fn update_animation_state(&mut self) {
269 self.animating = self.pipelines.values().any(PipelineDetails::animating);
270 self.webview.set_animating(self.animating());
271 }
272
273 pub(crate) fn on_vsync(&mut self) {
275 if let Some(fling_action) = self.touch_handler.on_vsync() {
276 self.on_scroll_window_event(
277 ScrollLocation::Delta(-fling_action.delta),
278 fling_action.cursor,
279 );
280 }
281 }
282
283 pub(crate) fn dispatch_input_event_with_hit_testing(&self, mut event: InputEventAndId) -> bool {
284 let event_point = event.event.point();
285 let hit_test_result = match event_point {
286 Some(point) => {
287 let hit_test_result = self
288 .global
289 .borrow()
290 .hit_test_at_point(point)
291 .into_iter()
292 .nth(0);
293 if hit_test_result.is_none() {
294 warn!("Empty hit test result for input event, ignoring.");
295 return false;
296 }
297 hit_test_result
298 },
299 None => None,
300 };
301
302 match event.event {
303 InputEvent::Touch(ref mut touch_event) => {
304 touch_event.init_sequence_id(self.touch_handler.current_sequence_id);
305 },
306 InputEvent::MouseMove(_) => {
307 self.global.borrow_mut().last_mouse_move_position = event_point;
308 },
309 InputEvent::MouseLeftViewport(_) => {
310 self.global.borrow_mut().last_mouse_move_position = None;
311 },
312 InputEvent::MouseButton(_) | InputEvent::Wheel(_) => {},
313 _ => unreachable!("Unexpected input event type: {event:?}"),
314 }
315
316 if let Err(error) = self.global.borrow().constellation_sender.send(
317 EmbedderToConstellationMessage::ForwardInputEvent(self.id, event, hit_test_result),
318 ) {
319 warn!("Sending event to constellation failed ({error:?}).");
320 false
321 } else {
322 true
323 }
324 }
325
326 pub(crate) fn notify_input_event(&mut self, event: InputEventAndId) {
327 if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
328 return;
329 }
330
331 if let InputEvent::Touch(event) = event.event {
332 self.on_touch_event(event);
333 return;
334 }
335
336 self.dispatch_input_event_with_hit_testing(event);
337 }
338
339 fn send_touch_event(&mut self, event: TouchEvent) -> bool {
340 self.dispatch_input_event_with_hit_testing(InputEvent::Touch(event).into())
341 }
342
343 pub(crate) fn on_touch_event(&mut self, event: TouchEvent) {
344 if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
345 return;
346 }
347
348 match event.event_type {
349 TouchEventType::Down => self.on_touch_down(event),
350 TouchEventType::Move => self.on_touch_move(event),
351 TouchEventType::Up => self.on_touch_up(event),
352 TouchEventType::Cancel => self.on_touch_cancel(event),
353 }
354 }
355
356 fn on_touch_down(&mut self, event: TouchEvent) {
357 self.touch_handler.on_touch_down(event.id, event.point);
358 self.send_touch_event(event);
359 }
360
361 fn on_touch_move(&mut self, mut event: TouchEvent) {
362 let action: TouchMoveAction = self.touch_handler.on_touch_move(event.id, event.point);
363 if TouchMoveAction::NoAction != action {
364 if self
367 .touch_handler
368 .move_allowed(self.touch_handler.current_sequence_id)
369 {
370 event.disable_cancelable();
372 match action {
373 TouchMoveAction::Scroll(delta, point) => self.on_scroll_window_event(
374 ScrollLocation::Delta(LayoutVector2D::from_untyped(delta.to_untyped())),
375 point.cast(),
376 ),
377 TouchMoveAction::Zoom(magnification, scroll_delta) => {
378 let cursor = Point2D::new(-1, -1); self.pending_scroll_zoom_events
384 .push(ScrollZoomEvent::PinchZoom(magnification));
385 self.pending_scroll_zoom_events
386 .push(ScrollZoomEvent::Scroll(ScrollEvent {
387 scroll_location: ScrollLocation::Delta(
388 LayoutVector2D::from_untyped(scroll_delta.to_untyped()),
389 ),
390 cursor,
391 event_count: 1,
392 }));
393 },
394 _ => {},
395 }
396 }
397 if !self
401 .touch_handler
402 .is_handling_touch_move(self.touch_handler.current_sequence_id) &&
403 self.send_touch_event(event) &&
404 event.is_cancelable()
405 {
406 self.touch_handler
407 .set_handling_touch_move(self.touch_handler.current_sequence_id, true);
408 }
409 }
410 }
411
412 fn on_touch_up(&mut self, event: TouchEvent) {
413 self.touch_handler.on_touch_up(event.id, event.point);
414 self.send_touch_event(event);
415 }
416
417 fn on_touch_cancel(&mut self, event: TouchEvent) {
418 self.touch_handler.on_touch_cancel(event.id, event.point);
420 self.send_touch_event(event);
421 }
422
423 pub(crate) fn on_touch_event_processed(&mut self, result: TouchEventResult) {
424 match result {
425 TouchEventResult::DefaultPrevented(sequence_id, event_type) => {
426 debug!(
427 "Touch event {:?} in sequence {:?} prevented!",
428 event_type, sequence_id
429 );
430 match event_type {
431 TouchEventType::Down => {
432 self.touch_handler.prevent_click(sequence_id);
434 self.touch_handler.prevent_move(sequence_id);
435 self.touch_handler
436 .remove_pending_touch_move_action(sequence_id);
437 },
438 TouchEventType::Move => {
439 if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) {
441 info.prevent_move = TouchMoveAllowed::Prevented;
442 if let TouchSequenceState::PendingFling { .. } = info.state {
443 info.state = TouchSequenceState::Finished;
444 }
445 self.touch_handler.set_handling_touch_move(
446 self.touch_handler.current_sequence_id,
447 false,
448 );
449 self.touch_handler
450 .remove_pending_touch_move_action(sequence_id);
451 }
452 },
453 TouchEventType::Up => {
454 let Some(info) =
458 &mut self.touch_handler.get_touch_sequence_mut(sequence_id)
459 else {
460 return;
465 };
466 match info.state {
467 TouchSequenceState::PendingClick(_) => {
468 info.state = TouchSequenceState::Finished;
469 self.touch_handler.remove_touch_sequence(sequence_id);
470 },
471 TouchSequenceState::Flinging { .. } => {
472 },
474 TouchSequenceState::Finished => {
475 self.touch_handler.remove_touch_sequence(sequence_id);
476 },
477 TouchSequenceState::Touching |
478 TouchSequenceState::Panning { .. } |
479 TouchSequenceState::Pinching |
480 TouchSequenceState::MultiTouch |
481 TouchSequenceState::PendingFling { .. } => {
482 },
487 }
488 },
489 TouchEventType::Cancel => {
490 self.touch_handler
493 .remove_pending_touch_move_action(sequence_id);
494 self.touch_handler.try_remove_touch_sequence(sequence_id);
495 },
496 }
497 },
498 TouchEventResult::DefaultAllowed(sequence_id, event_type) => {
499 debug!(
500 "Touch event {:?} in sequence {:?} allowed",
501 event_type, sequence_id
502 );
503 match event_type {
504 TouchEventType::Down => {},
505 TouchEventType::Move => {
506 if let Some(action) =
507 self.touch_handler.pending_touch_move_action(sequence_id)
508 {
509 match action {
510 TouchMoveAction::Scroll(delta, point) => self
511 .on_scroll_window_event(
512 ScrollLocation::Delta(LayoutVector2D::from_untyped(
513 delta.to_untyped(),
514 )),
515 point.cast(),
516 ),
517 TouchMoveAction::Zoom(magnification, scroll_delta) => {
518 let cursor = Point2D::new(-1, -1);
519 self.pending_scroll_zoom_events
524 .push(ScrollZoomEvent::PinchZoom(magnification));
525 self.pending_scroll_zoom_events
526 .push(ScrollZoomEvent::Scroll(ScrollEvent {
527 scroll_location: ScrollLocation::Delta(
528 LayoutVector2D::from_untyped(
529 scroll_delta.to_untyped(),
530 ),
531 ),
532 cursor,
533 event_count: 1,
534 }));
535 },
536 TouchMoveAction::NoAction => {
537 },
539 }
540 self.touch_handler
541 .remove_pending_touch_move_action(sequence_id);
542 }
543 self.touch_handler
544 .set_handling_touch_move(self.touch_handler.current_sequence_id, false);
545 if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) {
546 if info.prevent_move == TouchMoveAllowed::Pending {
547 info.prevent_move = TouchMoveAllowed::Allowed;
548 if let TouchSequenceState::PendingFling { velocity, cursor } =
549 info.state
550 {
551 info.state = TouchSequenceState::Flinging { velocity, cursor }
552 }
553 }
554 }
555 },
556 TouchEventType::Up => {
557 let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id)
558 else {
559 return;
561 };
562 match info.state {
563 TouchSequenceState::PendingClick(point) => {
564 info.state = TouchSequenceState::Finished;
565 if !info.prevent_click {
568 self.simulate_mouse_click(point);
569 }
570 self.touch_handler.remove_touch_sequence(sequence_id);
571 },
572 TouchSequenceState::Flinging { .. } => {
573 },
575 TouchSequenceState::Finished => {
576 self.touch_handler.remove_touch_sequence(sequence_id);
577 },
578 TouchSequenceState::Panning { .. } |
579 TouchSequenceState::Pinching |
580 TouchSequenceState::PendingFling { .. } => {
581 },
586 TouchSequenceState::MultiTouch | TouchSequenceState::Touching => {
587 },
589 }
590 },
591 TouchEventType::Cancel => {
592 self.touch_handler
593 .remove_pending_touch_move_action(sequence_id);
594 self.touch_handler.try_remove_touch_sequence(sequence_id);
595 },
596 }
597 },
598 }
599 }
600
601 fn simulate_mouse_click(&mut self, point: DevicePoint) {
603 let button = MouseButton::Left;
604 self.dispatch_input_event_with_hit_testing(
605 InputEvent::MouseMove(MouseMoveEvent::new(point)).into(),
606 );
607 self.dispatch_input_event_with_hit_testing(
608 InputEvent::MouseButton(MouseButtonEvent::new(
609 MouseButtonAction::Down,
610 button,
611 point,
612 ))
613 .into(),
614 );
615 self.dispatch_input_event_with_hit_testing(
616 InputEvent::MouseButton(MouseButtonEvent::new(MouseButtonAction::Up, button, point))
617 .into(),
618 );
619 }
620
621 pub(crate) fn notify_scroll_event(
622 &mut self,
623 scroll_location: ScrollLocation,
624 cursor: DeviceIntPoint,
625 ) {
626 if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
627 return;
628 }
629 self.on_scroll_window_event(scroll_location, cursor);
630 }
631
632 fn on_scroll_window_event(&mut self, scroll_location: ScrollLocation, cursor: DeviceIntPoint) {
633 self.pending_scroll_zoom_events
634 .push(ScrollZoomEvent::Scroll(ScrollEvent {
635 scroll_location,
636 cursor,
637 event_count: 1,
638 }));
639 }
640
641 pub(crate) fn process_pending_scroll_and_pinch_zoom_events(
648 &mut self,
649 ) -> (PinchZoomResult, Option<ScrollResult>) {
650 if self.pending_scroll_zoom_events.is_empty() {
651 return (PinchZoomResult::DidNotPinchZoom, None);
652 }
653
654 let mut combined_scroll_event: Option<ScrollEvent> = None;
656 let mut base_page_zoom = self.pinch_zoom_level().get();
657 let mut combined_magnification = 1.0;
658 for scroll_event in self.pending_scroll_zoom_events.drain(..) {
659 match scroll_event {
660 ScrollZoomEvent::PinchZoom(magnification) => {
661 combined_magnification *= magnification
662 },
663 ScrollZoomEvent::InitialViewportZoom(magnification) => {
664 base_page_zoom = magnification
665 },
666 ScrollZoomEvent::Scroll(scroll_event_info) => {
667 let combined_event = match combined_scroll_event.as_mut() {
668 None => {
669 combined_scroll_event = Some(scroll_event_info);
670 continue;
671 },
672 Some(combined_event) => combined_event,
673 };
674
675 match (
676 combined_event.scroll_location,
677 scroll_event_info.scroll_location,
678 ) {
679 (ScrollLocation::Delta(old_delta), ScrollLocation::Delta(new_delta)) => {
680 let old_event_count = Scale::new(combined_event.event_count as f32);
685 combined_event.event_count += 1;
686 let new_event_count = Scale::new(combined_event.event_count as f32);
687 combined_event.scroll_location = ScrollLocation::Delta(
688 (old_delta * old_event_count + new_delta) / new_event_count,
689 );
690 },
691 (ScrollLocation::Start, _) | (ScrollLocation::End, _) => {
692 break;
694 },
695 (_, ScrollLocation::Start) | (_, ScrollLocation::End) => {
696 *combined_event = scroll_event_info;
699 break;
700 },
701 }
702 },
703 }
704 }
705
706 let scroll_result = combined_scroll_event.and_then(|combined_event| {
707 self.scroll_node_at_device_point(
708 combined_event.cursor.to_f32(),
709 combined_event.scroll_location,
710 )
711 });
712 if let Some(scroll_result) = scroll_result.clone() {
713 self.send_scroll_positions_to_layout_for_pipeline(
714 scroll_result.hit_test_result.pipeline_id,
715 );
716 self.dispatch_scroll_event(
717 scroll_result.external_scroll_id,
718 scroll_result.hit_test_result,
719 );
720 }
721
722 let pinch_zoom_result =
723 match self.set_pinch_zoom_level(base_page_zoom * combined_magnification) {
724 true => PinchZoomResult::DidPinchZoom,
725 false => PinchZoomResult::DidNotPinchZoom,
726 };
727
728 (pinch_zoom_result, scroll_result)
729 }
730
731 fn scroll_node_at_device_point(
736 &mut self,
737 cursor: DevicePoint,
738 scroll_location: ScrollLocation,
739 ) -> Option<ScrollResult> {
740 let scroll_location = match scroll_location {
741 ScrollLocation::Delta(delta) => {
742 let device_pixels_per_page = self.device_pixels_per_page_pixel();
743 let scaled_delta = (Vector2D::from_untyped(delta.to_untyped()) /
744 device_pixels_per_page)
745 .to_untyped();
746 let calculated_delta = LayoutVector2D::from_untyped(scaled_delta);
747 ScrollLocation::Delta(calculated_delta)
748 },
749 ScrollLocation::Start | ScrollLocation::End => scroll_location,
751 };
752
753 let hit_test_results = self.global.borrow().hit_test_at_point(cursor);
754
755 let mut previous_pipeline_id = None;
759 for hit_test_result in hit_test_results.iter() {
760 let pipeline_details = self.pipelines.get_mut(&hit_test_result.pipeline_id)?;
761 if previous_pipeline_id.replace(&hit_test_result.pipeline_id) !=
762 Some(&hit_test_result.pipeline_id)
763 {
764 let scroll_result = pipeline_details.scroll_tree.scroll_node_or_ancestor(
765 hit_test_result.external_scroll_id,
766 scroll_location,
767 ScrollType::InputEvents,
768 );
769 if let Some((external_scroll_id, offset)) = scroll_result {
770 return Some(ScrollResult {
771 hit_test_result: hit_test_result.clone(),
772 external_scroll_id,
773 offset,
774 });
775 }
776 }
777 }
778 None
779 }
780
781 fn dispatch_scroll_event(
782 &self,
783 external_id: ExternalScrollId,
784 hit_test_result: CompositorHitTestResult,
785 ) {
786 let event = InputEvent::Scroll(EmbedderScrollEvent { external_id }).into();
787 let msg = EmbedderToConstellationMessage::ForwardInputEvent(
788 self.id,
789 event,
790 Some(hit_test_result),
791 );
792 if let Err(e) = self.global.borrow().constellation_sender.send(msg) {
793 warn!("Sending scroll event to constellation failed ({:?}).", e);
794 }
795 }
796
797 pub(crate) fn pinch_zoom_level(&self) -> Scale<f32, DevicePixel, DevicePixel> {
798 Scale::new(self.pinch_zoom.get())
799 }
800
801 fn set_pinch_zoom_level(&mut self, mut zoom: f32) -> bool {
802 if let Some(viewport) = self.viewport_description.as_ref() {
803 zoom = viewport.clamp_zoom(zoom);
804 }
805
806 let old_zoom = std::mem::replace(&mut self.pinch_zoom, PinchZoomFactor::new(zoom));
807 old_zoom != self.pinch_zoom
808 }
809
810 pub(crate) fn set_page_zoom(
811 &mut self,
812 new_page_zoom: Scale<f32, CSSPixel, DeviceIndependentPixel>,
813 ) {
814 let new_page_zoom = new_page_zoom.clamp(MIN_PAGE_ZOOM, MAX_PAGE_ZOOM);
815 let old_zoom = std::mem::replace(&mut self.page_zoom, new_page_zoom);
816 if old_zoom != self.page_zoom {
817 self.send_window_size_message();
818 }
819 }
820
821 pub(crate) fn device_pixels_per_page_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> {
827 let viewport_scale = self
828 .root_pipeline_id
829 .and_then(|pipeline_id| self.pipelines.get(&pipeline_id))
830 .and_then(|pipeline| pipeline.viewport_scale)
831 .unwrap_or_else(|| self.page_zoom * self.hidpi_scale_factor);
832 viewport_scale * self.pinch_zoom_level()
833 }
834
835 pub(crate) fn device_pixels_per_page_pixel_not_including_pinch_zoom(
840 &self,
841 ) -> Scale<f32, CSSPixel, DevicePixel> {
842 self.page_zoom * self.hidpi_scale_factor
843 }
844
845 pub(crate) fn set_pinch_zoom(&mut self, magnification: f32) {
847 if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
848 return;
849 }
850
851 self.pending_scroll_zoom_events
853 .push(ScrollZoomEvent::PinchZoom(
854 self.viewport_description
855 .clone()
856 .unwrap_or_default()
857 .clamp_zoom(magnification),
858 ));
859 }
860
861 fn send_window_size_message(&self) {
862 let device_pixel_ratio = self.device_pixels_per_page_pixel_not_including_pinch_zoom();
865 let initial_viewport = self.rect.size().to_f32() / device_pixel_ratio;
866 let msg = EmbedderToConstellationMessage::ChangeViewportDetails(
867 self.id,
868 ViewportDetails {
869 hidpi_scale_factor: device_pixel_ratio,
870 size: initial_viewport,
871 },
872 WindowSizeType::Resize,
873 );
874 if let Err(e) = self.global.borrow().constellation_sender.send(msg) {
875 warn!("Sending window resize to constellation failed ({:?}).", e);
876 }
877 }
878
879 pub(crate) fn set_hidpi_scale_factor(
881 &mut self,
882 new_scale: Scale<f32, DeviceIndependentPixel, DevicePixel>,
883 ) -> bool {
884 let old_scale_factor = std::mem::replace(&mut self.hidpi_scale_factor, new_scale);
885 if self.hidpi_scale_factor == old_scale_factor {
886 return false;
887 }
888
889 self.send_window_size_message();
890 true
891 }
892
893 pub(crate) fn set_rect(&mut self, new_rect: DeviceRect) -> bool {
895 let old_rect = std::mem::replace(&mut self.rect, new_rect);
896 if old_rect.size() != self.rect.size() {
897 self.send_window_size_message();
898 }
899 old_rect != self.rect
900 }
901
902 pub fn set_viewport_description(&mut self, viewport_description: ViewportDescription) {
903 self.pending_scroll_zoom_events
904 .push(ScrollZoomEvent::InitialViewportZoom(
905 viewport_description
906 .clone()
907 .clamp_zoom(viewport_description.initial_scale.get()),
908 ));
909 self.viewport_description = Some(viewport_description);
910 }
911
912 pub(crate) fn scroll_trees_memory_usage(
913 &self,
914 ops: &mut malloc_size_of::MallocSizeOfOps,
915 ) -> usize {
916 self.pipelines
917 .values()
918 .map(|pipeline| pipeline.scroll_tree.size_of(ops))
919 .sum::<usize>()
920 }
921}
922
923#[derive(Clone, Copy, Debug, PartialEq)]
924pub struct UnknownWebView(pub WebViewId);