1use std::cell::{Cell, Ref, RefCell, RefMut};
6use std::hash::Hash;
7use std::rc::{Rc, Weak};
8
9use accesskit::{
10 Affine as AccesskitAffine, Node as AccesskitNode, NodeId, Rect as AccesskitRect, Role, Tree,
11 TreeId, TreeUpdate, Uuid as AccesskitUuid,
12};
13use dpi::PhysicalSize;
14use embedder_traits::{
15 ContextMenuAction, ContextMenuItem, Cursor, EmbedderControlId, EmbedderControlRequest, Image,
16 InputEvent, InputEventAndId, InputEventId, JSValue, JavaScriptEvaluationError, LoadStatus,
17 MediaSessionActionType, NewWebViewDetails, ScreenGeometry, ScreenshotCaptureError, Scroll,
18 Theme, TraversalId, UrlRequest, ViewportDetails, WebViewPoint, WebViewRect,
19};
20use euclid::{Scale, Size2D};
21use image::RgbaImage;
22use log::debug;
23use paint_api::WebViewTrait;
24use paint_api::rendering_context::RenderingContext;
25use servo_base::Epoch;
26use servo_base::generic_channel::GenericSender;
27use servo_base::id::WebViewId;
28use servo_config::pref;
29use servo_constellation_traits::{
30 EmbedderToConstellationMessage, HistoryTraversalSource, SessionHistoryTraversalRequest,
31 TraversalDirection,
32};
33use servo_geometry::DeviceIndependentPixel;
34use servo_url::ServoUrl;
35use style_traits::CSSPixel;
36use url::Url;
37use webrender_api::units::{DeviceIntRect, DevicePixel, DevicePoint, DeviceSize};
38
39use crate::clipboard_delegate::{ClipboardDelegate, DefaultClipboardDelegate};
40#[cfg(feature = "gamepad")]
41use crate::gamepad_delegate::{DefaultGamepadDelegate, GamepadDelegate};
42use crate::responders::AutomaticResponder;
43use crate::servo::PendingHandledInputEvent;
44use crate::webview_delegate::{CreateNewWebViewRequest, DefaultWebViewDelegate, WebViewDelegate};
45use crate::{
46 ColorPicker, ContextMenu, EmbedderControl, InputMethodControl, SelectElement, Servo,
47 UserContentManager, WebRenderDebugOption,
48};
49
50pub(crate) const MINIMUM_WEBVIEW_SIZE: Size2D<i32, DevicePixel> = Size2D::new(1, 1);
51
52#[derive(Clone)]
84pub struct WebView(Rc<RefCell<WebViewInner>>);
85
86impl PartialEq for WebView {
87 fn eq(&self, other: &Self) -> bool {
88 self.inner().id == other.inner().id
89 }
90}
91
92impl Hash for WebView {
93 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
94 self.inner().id.hash(state);
95 }
96}
97
98pub(crate) struct WebViewInner {
99 pub(crate) id: WebViewId,
100 pub(crate) servo: Servo,
101 pub(crate) delegate: Rc<dyn WebViewDelegate>,
102 pub(crate) clipboard_delegate: Rc<dyn ClipboardDelegate>,
103 #[cfg(feature = "gamepad")]
104 pub(crate) gamepad_delegate: Rc<dyn GamepadDelegate>,
105
106 pub(crate) accesskit_tree_id: Option<TreeId>,
111 pub(crate) grafted_accesskit_tree_id: Option<TreeId>,
114 grafted_accesskit_tree_epoch: Option<Epoch>,
117 accessibility_viewport_changed: Cell<bool>,
125
126 rendering_context: Rc<dyn RenderingContext>,
127 user_content_manager: Option<Rc<UserContentManager>>,
128 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
129 load_status: LoadStatus,
130 status_text: Option<String>,
131 page_title: Option<String>,
132 favicon: Option<Image>,
133 focused: bool,
134 animating: bool,
135 cursor: Cursor,
136
137 back_forward_list: Vec<Url>,
139
140 back_forward_list_index: usize,
142}
143
144impl Drop for WebViewInner {
145 fn drop(&mut self) {
146 self.servo
147 .constellation_proxy()
148 .send(EmbedderToConstellationMessage::CloseWebView(self.id));
149 self.servo.paint_mut().remove_webview(self.id);
150 }
151}
152
153impl WebView {
154 pub(crate) fn new(mut builder: WebViewBuilder) -> Self {
155 let servo = builder.servo;
156 let painter_id = servo
157 .paint_mut()
158 .register_rendering_context(builder.rendering_context.clone());
159
160 let id = WebViewId::new(painter_id);
161 let webview = Self(Rc::new(RefCell::new(WebViewInner {
162 id,
163 servo: servo.clone(),
164 rendering_context: builder.rendering_context,
165 delegate: builder.delegate,
166 clipboard_delegate: builder
167 .clipboard_delegate
168 .unwrap_or_else(|| Rc::new(DefaultClipboardDelegate)),
169 #[cfg(feature = "gamepad")]
170 gamepad_delegate: builder
171 .gamepad_delegate
172 .unwrap_or_else(|| Rc::new(DefaultGamepadDelegate)),
173 accesskit_tree_id: None,
174 grafted_accesskit_tree_id: None,
175 grafted_accesskit_tree_epoch: None,
176 accessibility_viewport_changed: Cell::new(false),
177 hidpi_scale_factor: builder.hidpi_scale_factor,
178 load_status: LoadStatus::Started,
179 status_text: None,
180 page_title: None,
181 favicon: None,
182 focused: false,
183 animating: false,
184 cursor: Cursor::Pointer,
185 back_forward_list: Default::default(),
186 back_forward_list_index: 0,
187 user_content_manager: builder.user_content_manager.clone(),
188 })));
189
190 let viewport_details = webview.viewport_details();
191 servo.paint().add_webview(
192 Box::new(ServoRendererWebView {
193 weak_handle: webview.weak_handle(),
194 id,
195 }),
196 viewport_details,
197 );
198
199 servo
200 .webviews_mut()
201 .insert(webview.id(), webview.weak_handle());
202
203 let user_content_manager_id = builder
204 .user_content_manager
205 .as_ref()
206 .map(|user_content_manager| user_content_manager.id());
207
208 let new_webview_details = NewWebViewDetails {
209 webview_id: webview.id(),
210 viewport_details,
211 user_content_manager_id,
212 };
213
214 match builder.create_new_webview_responder.as_mut() {
220 Some(responder) => {
221 let _ = responder.send(Some(new_webview_details));
222 },
223 None => {
224 let url = builder.url.unwrap_or(
225 Url::parse("about:blank")
226 .expect("Should always be able to parse 'about:blank'."),
227 );
228
229 servo
230 .constellation_proxy()
231 .send(EmbedderToConstellationMessage::NewWebView(
232 url.into(),
233 new_webview_details,
234 ));
235 },
236 }
237
238 webview
239 }
240
241 fn inner(&self) -> Ref<'_, WebViewInner> {
242 self.0.borrow()
243 }
244
245 fn inner_mut(&self) -> RefMut<'_, WebViewInner> {
246 self.0.borrow_mut()
247 }
248
249 pub(crate) fn request_create_new(
250 &self,
251 response_sender: GenericSender<Option<NewWebViewDetails>>,
252 ) {
253 let request = CreateNewWebViewRequest {
254 servo: self.inner().servo.clone(),
255 responder: AutomaticResponder::new(response_sender, None),
256 };
257 self.delegate().request_create_new(self.clone(), request);
258 }
259
260 pub(crate) fn viewport_details(&self) -> ViewportDetails {
261 let inner = self.inner();
264 let viewport_size = inner.rendering_context.size2d().to_f32();
265 let scaled_viewport_size = viewport_size / inner.hidpi_scale_factor;
266 let device_size = self
267 .delegate()
268 .screen_geometry(self.clone())
269 .map(|geometry| geometry.size.to_f32())
270 .unwrap_or_else(|| viewport_size);
271 ViewportDetails {
272 size: scaled_viewport_size / Scale::new(1.0),
273 hidpi_scale_factor: Scale::new(inner.hidpi_scale_factor.0),
274 device_size,
275 }
276 }
277
278 pub(crate) fn from_weak_handle(inner: &Weak<RefCell<WebViewInner>>) -> Option<Self> {
279 inner.upgrade().map(WebView)
280 }
281
282 pub(crate) fn weak_handle(&self) -> Weak<RefCell<WebViewInner>> {
283 Rc::downgrade(&self.0)
284 }
285
286 pub fn delegate(&self) -> Rc<dyn WebViewDelegate> {
288 self.inner().delegate.clone()
289 }
290
291 pub fn clipboard_delegate(&self) -> Rc<dyn ClipboardDelegate> {
293 self.inner().clipboard_delegate.clone()
294 }
295
296 #[cfg(feature = "gamepad")]
298 pub fn gamepad_delegate(&self) -> Rc<dyn GamepadDelegate> {
299 self.inner().gamepad_delegate.clone()
300 }
301
302 pub fn id(&self) -> WebViewId {
304 self.inner().id
305 }
306
307 pub fn rendering_context(&self) -> Rc<dyn RenderingContext> {
309 self.inner().rendering_context.clone()
310 }
311
312 pub fn load_status(&self) -> LoadStatus {
317 self.inner().load_status
318 }
319
320 pub(crate) fn set_load_status(self, new_value: LoadStatus) {
321 if self.inner().load_status == new_value {
322 return;
323 }
324 self.inner_mut().load_status = new_value;
325 self.delegate().notify_load_status_changed(self, new_value);
326 }
327
328 pub fn url(&self) -> Option<Url> {
331 let inner = self.inner();
332 inner
333 .back_forward_list
334 .get(inner.back_forward_list_index)
335 .cloned()
336 }
337
338 pub fn status_text(&self) -> Option<String> {
344 self.inner().status_text.clone()
345 }
346
347 pub(crate) fn set_status_text(self, new_value: Option<String>) {
348 if self.inner().status_text == new_value {
349 return;
350 }
351 self.inner_mut().status_text = new_value.clone();
352 self.delegate().notify_status_text_changed(self, new_value);
353 }
354
355 pub fn page_title(&self) -> Option<String> {
361 self.inner().page_title.clone()
362 }
363
364 pub(crate) fn set_page_title(self, new_value: Option<String>) {
365 if self.inner().page_title == new_value {
366 return;
367 }
368 self.inner_mut().page_title = new_value.clone();
369 self.delegate().notify_page_title_changed(self, new_value);
370 }
371
372 pub fn favicon(&self) -> Option<Ref<'_, Image>> {
379 Ref::filter_map(self.inner(), |inner| inner.favicon.as_ref()).ok()
380 }
381
382 pub(crate) fn set_favicon(self, new_value: Image) {
383 self.inner_mut().favicon = Some(new_value);
384 self.delegate().notify_favicon_changed(self);
385 }
386
387 pub fn focused(&self) -> bool {
392 self.inner().focused
393 }
394
395 pub(crate) fn set_focused(self, new_value: bool) {
396 if self.inner().focused == new_value {
397 return;
398 }
399 self.inner_mut().focused = new_value;
400 self.delegate().notify_focus_changed(self, new_value);
401 }
402
403 pub fn cursor(&self) -> Cursor {
409 self.inner().cursor
410 }
411
412 pub(crate) fn set_cursor(self, new_value: Cursor) {
413 if self.inner().cursor == new_value {
414 return;
415 }
416 self.inner_mut().cursor = new_value;
417 self.delegate().notify_cursor_changed(self, new_value);
418 }
419
420 pub fn focus(&self) {
422 self.inner()
423 .servo
424 .constellation_proxy()
425 .send(EmbedderToConstellationMessage::FocusWebView(self.id()));
426 }
427
428 pub fn blur(&self) {
430 self.inner()
431 .servo
432 .constellation_proxy()
433 .send(EmbedderToConstellationMessage::BlurWebView);
434 }
435
436 pub fn animating(&self) -> bool {
441 self.inner().animating
442 }
443
444 pub(crate) fn set_animating(self, new_value: bool) {
445 if self.inner().animating == new_value {
446 return;
447 }
448 self.inner_mut().animating = new_value;
449 self.delegate().notify_animating_changed(self, new_value);
450 }
451
452 pub fn size(&self) -> DeviceSize {
454 self.inner().rendering_context.size2d().to_f32()
455 }
456
457 pub fn resize(&self, new_size: PhysicalSize<u32>) {
464 let new_size = PhysicalSize {
465 width: new_size.width.max(MINIMUM_WEBVIEW_SIZE.width as u32),
466 height: new_size.height.max(MINIMUM_WEBVIEW_SIZE.height as u32),
467 };
468
469 self.inner()
470 .servo
471 .paint()
472 .resize_rendering_context(self.id(), new_size);
473 }
474
475 pub fn hidpi_scale_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> {
477 self.inner().hidpi_scale_factor
478 }
479
480 pub fn set_hidpi_scale_factor(
485 &self,
486 new_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
487 ) {
488 if self.inner().hidpi_scale_factor == new_scale_factor {
489 return;
490 }
491
492 self.inner_mut().hidpi_scale_factor = new_scale_factor;
493 self.inner()
494 .servo
495 .paint()
496 .set_hidpi_scale_factor(self.id(), new_scale_factor);
497 }
498
499 pub fn show(&self) {
501 self.inner()
502 .servo
503 .paint()
504 .show_webview(self.id())
505 .expect("BUG: invalid WebView instance");
506 }
507
508 pub fn hide(&self) {
510 self.inner()
511 .servo
512 .paint()
513 .hide_webview(self.id())
514 .expect("BUG: invalid WebView instance");
515 }
516
517 pub fn notify_theme_change(&self, theme: Theme) {
519 self.inner()
520 .servo
521 .constellation_proxy()
522 .send(EmbedderToConstellationMessage::ThemeChange(
523 self.id(),
524 theme,
525 ))
526 }
527
528 pub fn load(&self, url: Url) {
533 self.inner()
534 .servo
535 .constellation_proxy()
536 .send(EmbedderToConstellationMessage::LoadUrl(
537 self.id(),
538 UrlRequest::new(url),
539 ))
540 }
541
542 pub fn load_request(&self, url_request: UrlRequest) {
547 self.inner()
548 .servo
549 .constellation_proxy()
550 .send(EmbedderToConstellationMessage::LoadUrl(
551 self.id(),
552 url_request,
553 ))
554 }
555
556 pub fn reload(&self) {
558 self.inner_mut().load_status = LoadStatus::Started;
559 self.inner()
560 .servo
561 .constellation_proxy()
562 .send(EmbedderToConstellationMessage::Reload(self.id()))
563 }
564
565 pub fn can_go_back(&self) -> bool {
570 self.inner().back_forward_list_index != 0
571 }
572
573 pub fn go_back(&self, amount: usize) -> TraversalId {
579 let request = SessionHistoryTraversalRequest::new(
580 self.id(),
581 TraversalDirection::Back(amount),
582 HistoryTraversalSource::Embedder,
583 );
584 let traversal_id = request.id.clone();
585 self.inner()
586 .servo
587 .constellation_proxy()
588 .send(EmbedderToConstellationMessage::TraverseHistory(request));
589 traversal_id
590 }
591
592 pub fn can_go_forward(&self) -> bool {
597 let inner = self.inner();
598 inner.back_forward_list.len() > inner.back_forward_list_index + 1
599 }
600
601 pub fn go_forward(&self, amount: usize) -> TraversalId {
607 let request = SessionHistoryTraversalRequest::new(
608 self.id(),
609 TraversalDirection::Forward(amount),
610 HistoryTraversalSource::Embedder,
611 );
612 let traversal_id = request.id.clone();
613 self.inner()
614 .servo
615 .constellation_proxy()
616 .send(EmbedderToConstellationMessage::TraverseHistory(request));
617 traversal_id
618 }
619
620 pub fn notify_scroll_event(&self, scroll: Scroll, point: WebViewPoint) {
623 self.inner()
624 .servo
625 .paint()
626 .notify_scroll_event(self.id(), scroll, point);
627 }
628
629 pub fn notify_input_event(&self, event: InputEvent) -> InputEventId {
636 let event: InputEventAndId = event.into();
637 let event_id = event.id;
638 let webview_id = self.id();
639 let servo = &self.inner().servo;
640 if event.event.point().is_some() {
642 if !servo.paint().notify_input_event(self.id(), event) {
643 servo.add_pending_handled_input_event(PendingHandledInputEvent {
644 event_id,
645 webview_id,
646 });
647 servo.event_loop_waker().wake();
648 }
649 } else {
650 servo
651 .constellation_proxy()
652 .send(EmbedderToConstellationMessage::ForwardInputEvent(
653 webview_id, event, None, ));
655 }
656
657 event_id
658 }
659
660 pub fn notify_media_session_action_event(&self, event: MediaSessionActionType) {
662 self.inner()
663 .servo
664 .constellation_proxy()
665 .send(EmbedderToConstellationMessage::MediaSessionAction(event));
666 }
667
668 pub fn set_page_zoom(&self, new_zoom: f32) {
678 self.inner()
679 .servo
680 .paint()
681 .set_page_zoom(self.id(), new_zoom);
682 }
683
684 pub fn page_zoom(&self) -> f32 {
686 self.inner().servo.paint().page_zoom(self.id())
687 }
688
689 pub fn adjust_pinch_zoom(&self, pinch_zoom_delta: f32, center: DevicePoint) {
700 self.inner()
701 .servo
702 .paint()
703 .adjust_pinch_zoom(self.id(), pinch_zoom_delta, center);
704 }
705
706 pub fn pinch_zoom(&self) -> f32 {
708 self.inner().servo.paint().pinch_zoom(self.id())
709 }
710
711 pub fn device_pixels_per_css_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> {
716 self.inner()
717 .servo
718 .paint()
719 .device_pixels_per_page_pixel(self.id())
720 }
721
722 pub fn exit_fullscreen(&self) {
724 self.inner()
725 .servo
726 .constellation_proxy()
727 .send(EmbedderToConstellationMessage::ExitFullScreen(self.id()));
728 }
729
730 pub fn set_throttled(&self, throttled: bool) {
735 self.inner().servo.constellation_proxy().send(
736 EmbedderToConstellationMessage::SetWebViewThrottled(self.id(), throttled),
737 );
738 }
739
740 pub fn toggle_webrender_debugging(&self, debugging: WebRenderDebugOption) {
746 self.inner().servo.paint().toggle_webrender_debug(debugging);
747 }
748
749 pub fn capture_webrender(&self) {
754 self.inner().servo.paint().capture_webrender(self.id());
755 }
756
757 pub fn paint(&self) {
759 self.inner().servo.paint().render(self.id());
760 }
761
762 pub fn user_content_manager(&self) -> Option<Rc<UserContentManager>> {
764 self.inner().user_content_manager.clone()
765 }
766
767 pub fn evaluate_javascript<T: ToString>(
770 &self,
771 script: T,
772 callback: impl FnOnce(Result<JSValue, JavaScriptEvaluationError>) + 'static,
773 ) {
774 self.inner().servo.javascript_evaluator_mut().evaluate(
775 self.id(),
776 script.to_string(),
777 Box::new(callback),
778 );
779 }
780
781 pub fn take_screenshot(
799 &self,
800 rect: Option<WebViewRect>,
801 callback: impl FnOnce(Result<RgbaImage, ScreenshotCaptureError>) + 'static,
802 ) {
803 self.inner()
804 .servo
805 .paint()
806 .request_screenshot(self.id(), rect, Box::new(callback));
807 }
808
809 pub(crate) fn set_history(self, new_back_forward_list: Vec<ServoUrl>, new_index: usize) {
810 {
811 let mut inner_mut = self.inner_mut();
812 inner_mut.back_forward_list_index = new_index;
813 inner_mut.back_forward_list = new_back_forward_list
814 .into_iter()
815 .map(ServoUrl::into_url)
816 .collect();
817 }
818
819 let back_forward_list = self.inner().back_forward_list.clone();
820 let back_forward_list_index = self.inner().back_forward_list_index;
821 self.delegate().notify_url_changed(
822 self.clone(),
823 back_forward_list[back_forward_list_index].clone(),
824 );
825 self.delegate().notify_history_changed(
826 self.clone(),
827 back_forward_list,
828 back_forward_list_index,
829 );
830 }
831
832 pub(crate) fn show_embedder_control(
833 self,
834 control_id: EmbedderControlId,
835 position: DeviceIntRect,
836 embedder_control_request: EmbedderControlRequest,
837 ) {
838 let constellation_proxy = self.inner().servo.constellation_proxy().clone();
839 let embedder_control = match embedder_control_request {
840 EmbedderControlRequest::SelectElement(request) => {
841 EmbedderControl::SelectElement(SelectElement {
842 id: control_id,
843 select_element_request: request,
844 position,
845 constellation_proxy,
846 response_sent: false,
847 })
848 },
849 EmbedderControlRequest::ColorPicker(current_color) => {
850 EmbedderControl::ColorPicker(ColorPicker {
851 id: control_id,
852 current_color: Some(current_color),
853 position,
854 constellation_proxy,
855 response_sent: false,
856 })
857 },
858 EmbedderControlRequest::InputMethod(input_method_request) => {
859 EmbedderControl::InputMethod(InputMethodControl {
860 id: control_id,
861 input_method_type: input_method_request.input_method_type,
862 text: input_method_request.text,
863 insertion_point: input_method_request.insertion_point,
864 position,
865 multiline: input_method_request.multiline,
866 allow_virtual_keyboard: input_method_request.allow_virtual_keyboard,
867 })
868 },
869 EmbedderControlRequest::ContextMenu(mut context_menu_request) => {
870 for item in context_menu_request.items.iter_mut() {
871 match item {
872 ContextMenuItem::Item {
873 action: ContextMenuAction::GoBack,
874 enabled,
875 ..
876 } => *enabled = self.can_go_back(),
877 ContextMenuItem::Item {
878 action: ContextMenuAction::GoForward,
879 enabled,
880 ..
881 } => *enabled = self.can_go_forward(),
882 _ => {},
883 }
884 }
885 EmbedderControl::ContextMenu(ContextMenu {
886 id: control_id,
887 position,
888 items: context_menu_request.items,
889 element_info: context_menu_request.element_info,
890 constellation_proxy,
891 response_sent: false,
892 })
893 },
894 EmbedderControlRequest::FilePicker { .. } => {
895 unreachable!("This message should be routed through the FileManagerThread")
896 },
897 };
898
899 self.delegate()
900 .show_embedder_control(self.clone(), embedder_control);
901 }
902
903 pub fn accesskit_tree_id(&self) -> Option<TreeId> {
905 self.inner().accesskit_tree_id
906 }
907
908 pub fn set_accessibility_active(&self, active: bool) -> Option<TreeId> {
923 if !pref!(accessibility_enabled) {
924 return None;
925 }
926
927 if active == self.inner().accesskit_tree_id.is_some() {
928 return self.accesskit_tree_id();
929 }
930
931 if active {
932 let accesskit_tree_id = TreeId(AccesskitUuid::new_v4());
933 self.inner_mut().accesskit_tree_id = Some(accesskit_tree_id);
934 } else {
935 self.inner_mut().accesskit_tree_id = None;
936 self.inner_mut().grafted_accesskit_tree_id = None;
937 self.inner_mut().grafted_accesskit_tree_epoch = None;
938 }
939
940 self.inner().servo.constellation_proxy().send(
941 EmbedderToConstellationMessage::SetAccessibilityActive(self.id(), active),
942 );
943
944 self.accesskit_tree_id()
945 }
946
947 pub(crate) fn notify_document_accessibility_tree_id(&self, grafted_tree_id: TreeId) {
948 let old_grafted_tree_id = self
949 .inner_mut()
950 .grafted_accesskit_tree_id
951 .replace(grafted_tree_id);
952 if old_grafted_tree_id == Some(grafted_tree_id) {
955 return;
956 }
957 self.send_accessibility_root_node();
958 }
959
960 pub(crate) fn note_accessibility_viewport_changed(&self) {
969 self.inner().accessibility_viewport_changed.set(true);
970 }
971
972 pub(crate) fn take_accessibility_viewport_changed(&self) -> bool {
974 self.inner().accessibility_viewport_changed.take()
975 }
976
977 pub(crate) fn send_accessibility_root_node(&self) {
984 let Some(webview_accesskit_tree_id) = self.inner().accesskit_tree_id else {
985 return;
986 };
987 let Some(grafted_tree_id) = self.inner().grafted_accesskit_tree_id else {
988 return;
989 };
990 let root_node_id = NodeId(0);
991 let mut root_node = AccesskitNode::new(Role::ScrollView);
992 let device_pixels_per_css_pixel =
1005 self.page_zoom() * self.hidpi_scale_factor().get() * self.pinch_zoom();
1006 let size =
1007 self.size() / Scale::<f32, CSSPixel, DevicePixel>::new(device_pixels_per_css_pixel);
1008 root_node.set_bounds(AccesskitRect::new(
1009 0.0,
1010 0.0,
1011 size.width as f64,
1012 size.height as f64,
1013 ));
1014 if device_pixels_per_css_pixel != 1.0 {
1016 root_node.set_transform(AccesskitAffine::scale(device_pixels_per_css_pixel as f64));
1017 }
1018 let graft_node_id = NodeId(1);
1019 let mut graft_node = AccesskitNode::new(Role::GenericContainer);
1020 graft_node.set_tree_id(grafted_tree_id);
1021 root_node.set_children(vec![graft_node_id]);
1022 self.delegate().notify_accessibility_tree_update(
1023 self.clone(),
1024 TreeUpdate {
1025 nodes: vec![(root_node_id, root_node), (graft_node_id, graft_node)],
1026 tree: Some(Tree {
1027 root: root_node_id,
1028 toolkit_name: None,
1029 toolkit_version: None,
1030 }),
1031 tree_id: webview_accesskit_tree_id,
1032 focus: root_node_id,
1033 },
1034 );
1035 }
1036
1037 pub(crate) fn process_accessibility_tree_update(&self, tree_update: TreeUpdate, epoch: Epoch) {
1038 if self
1039 .inner()
1040 .grafted_accesskit_tree_epoch
1041 .is_some_and(|current| epoch < current)
1042 {
1043 debug!("Ignoring stale tree update for {:?}", tree_update.tree_id);
1047 return;
1048 }
1049 if self
1050 .inner()
1051 .grafted_accesskit_tree_epoch
1052 .is_none_or(|current| epoch > current)
1053 {
1054 self.notify_document_accessibility_tree_id(tree_update.tree_id);
1055 self.inner_mut().grafted_accesskit_tree_epoch = Some(epoch);
1056 }
1057 self.delegate()
1058 .notify_accessibility_tree_update(self.clone(), tree_update);
1059 }
1060
1061 pub fn clear_session_history(&self) {
1066 self.inner().servo.constellation_proxy().send(
1067 EmbedderToConstellationMessage::ClearSessionHistory(self.id()),
1068 );
1069 }
1070}
1071
1072struct ServoRendererWebView {
1075 id: WebViewId,
1076 weak_handle: Weak<RefCell<WebViewInner>>,
1077}
1078
1079impl WebViewTrait for ServoRendererWebView {
1080 fn id(&self) -> WebViewId {
1081 self.id
1082 }
1083
1084 fn screen_geometry(&self) -> Option<ScreenGeometry> {
1085 let webview = WebView::from_weak_handle(&self.weak_handle)?;
1086 webview.delegate().screen_geometry(webview)
1087 }
1088
1089 fn set_animating(&self, new_value: bool) {
1090 if let Some(webview) = WebView::from_weak_handle(&self.weak_handle) {
1091 webview.set_animating(new_value);
1092 }
1093 }
1094
1095 fn notify_viewport_updated(&self) {
1096 if let Some(webview) = WebView::from_weak_handle(&self.weak_handle) {
1097 webview.note_accessibility_viewport_changed();
1098 }
1099 }
1100}
1101
1102pub struct WebViewBuilder {
1104 servo: Servo,
1105 rendering_context: Rc<dyn RenderingContext>,
1106 delegate: Rc<dyn WebViewDelegate>,
1107 url: Option<Url>,
1108 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1109 create_new_webview_responder: Option<AutomaticResponder<Option<NewWebViewDetails>>>,
1110 user_content_manager: Option<Rc<UserContentManager>>,
1111 clipboard_delegate: Option<Rc<dyn ClipboardDelegate>>,
1112 #[cfg(feature = "gamepad")]
1113 gamepad_delegate: Option<Rc<dyn GamepadDelegate>>,
1114}
1115
1116impl WebViewBuilder {
1117 pub fn new(servo: &Servo, rendering_context: Rc<dyn RenderingContext>) -> Self {
1122 Self {
1123 servo: servo.clone(),
1124 rendering_context,
1125 url: None,
1126 hidpi_scale_factor: Scale::new(1.0),
1127 delegate: Rc::new(DefaultWebViewDelegate),
1128 create_new_webview_responder: None,
1129 user_content_manager: None,
1130 clipboard_delegate: None,
1131 #[cfg(feature = "gamepad")]
1132 gamepad_delegate: None,
1133 }
1134 }
1135
1136 pub(crate) fn new_for_create_request(
1137 servo: &Servo,
1138 rendering_context: Rc<dyn RenderingContext>,
1139 responder: AutomaticResponder<Option<NewWebViewDetails>>,
1140 ) -> Self {
1141 let mut builder = Self::new(servo, rendering_context);
1142 builder.create_new_webview_responder = Some(responder);
1143 builder
1144 }
1145
1146 pub fn delegate(mut self, delegate: Rc<dyn WebViewDelegate>) -> Self {
1149 self.delegate = delegate;
1150 self
1151 }
1152
1153 pub fn url(mut self, url: Url) -> Self {
1155 self.url = Some(url);
1156 self
1157 }
1158
1159 pub fn hidpi_scale_factor(
1161 mut self,
1162 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1163 ) -> Self {
1164 self.hidpi_scale_factor = hidpi_scale_factor;
1165 self
1166 }
1167
1168 pub fn user_content_manager(mut self, user_content_manager: Rc<UserContentManager>) -> Self {
1172 self.user_content_manager = Some(user_content_manager);
1173 self
1174 }
1175
1176 pub fn clipboard_delegate(mut self, clipboard_delegate: Rc<dyn ClipboardDelegate>) -> Self {
1179 self.clipboard_delegate = Some(clipboard_delegate);
1180 self
1181 }
1182
1183 #[cfg(feature = "gamepad")]
1186 pub fn gamepad_delegate(mut self, gamepad_delegate: Rc<dyn GamepadDelegate>) -> Self {
1187 self.gamepad_delegate = Some(gamepad_delegate);
1188 self
1189 }
1190
1191 pub fn build(self) -> WebView {
1193 WebView::new(self)
1194 }
1195}