1use std::cell::{Ref, RefCell, RefMut};
6use std::hash::Hash;
7use std::rc::{Rc, Weak};
8
9use accesskit::{
10 Node as AccesskitNode, NodeId, Role, Tree, TreeId, TreeUpdate, Uuid as AccesskitUuid,
11};
12use dpi::PhysicalSize;
13use embedder_traits::{
14 ContextMenuAction, ContextMenuItem, Cursor, EmbedderControlId, EmbedderControlRequest, Image,
15 InputEvent, InputEventAndId, InputEventId, JSValue, JavaScriptEvaluationError, LoadStatus,
16 MediaSessionActionType, NewWebViewDetails, ScreenGeometry, ScreenshotCaptureError, Scroll,
17 Theme, TraversalId, UrlRequest, ViewportDetails, WebViewPoint, WebViewRect,
18};
19use euclid::{Scale, Size2D};
20use image::RgbaImage;
21use log::debug;
22use paint_api::WebViewTrait;
23use paint_api::rendering_context::RenderingContext;
24use servo_base::Epoch;
25use servo_base::generic_channel::GenericSender;
26use servo_base::id::WebViewId;
27use servo_config::pref;
28use servo_constellation_traits::{
29 EmbedderToConstellationMessage, HistoryTraversalSource, SessionHistoryTraversalRequest,
30 TraversalDirection,
31};
32use servo_geometry::DeviceIndependentPixel;
33use servo_url::ServoUrl;
34use style_traits::CSSPixel;
35use url::Url;
36use webrender_api::units::{DeviceIntRect, DevicePixel, DevicePoint, DeviceSize};
37
38use crate::clipboard_delegate::{ClipboardDelegate, DefaultClipboardDelegate};
39#[cfg(feature = "gamepad")]
40use crate::gamepad_delegate::{DefaultGamepadDelegate, GamepadDelegate};
41use crate::responders::IpcResponder;
42use crate::servo::PendingHandledInputEvent;
43use crate::webview_delegate::{CreateNewWebViewRequest, DefaultWebViewDelegate, WebViewDelegate};
44use crate::{
45 ColorPicker, ContextMenu, EmbedderControl, InputMethodControl, SelectElement, Servo,
46 UserContentManager, WebRenderDebugOption,
47};
48
49pub(crate) const MINIMUM_WEBVIEW_SIZE: Size2D<i32, DevicePixel> = Size2D::new(1, 1);
50
51#[derive(Clone)]
83pub struct WebView(Rc<RefCell<WebViewInner>>);
84
85impl PartialEq for WebView {
86 fn eq(&self, other: &Self) -> bool {
87 self.inner().id == other.inner().id
88 }
89}
90
91impl Hash for WebView {
92 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
93 self.inner().id.hash(state);
94 }
95}
96
97pub(crate) struct WebViewInner {
98 pub(crate) id: WebViewId,
99 pub(crate) servo: Servo,
100 pub(crate) delegate: Rc<dyn WebViewDelegate>,
101 pub(crate) clipboard_delegate: Rc<dyn ClipboardDelegate>,
102 #[cfg(feature = "gamepad")]
103 pub(crate) gamepad_delegate: Rc<dyn GamepadDelegate>,
104
105 pub(crate) accesskit_tree_id: Option<TreeId>,
110 pub(crate) grafted_accesskit_tree_id: Option<TreeId>,
113 grafted_accesskit_tree_epoch: Option<Epoch>,
116
117 rendering_context: Rc<dyn RenderingContext>,
118 user_content_manager: Option<Rc<UserContentManager>>,
119 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
120 load_status: LoadStatus,
121 status_text: Option<String>,
122 page_title: Option<String>,
123 favicon: Option<Image>,
124 focused: bool,
125 animating: bool,
126 cursor: Cursor,
127
128 back_forward_list: Vec<Url>,
130
131 back_forward_list_index: usize,
133}
134
135impl Drop for WebViewInner {
136 fn drop(&mut self) {
137 self.servo
138 .constellation_proxy()
139 .send(EmbedderToConstellationMessage::CloseWebView(self.id));
140 self.servo.paint_mut().remove_webview(self.id);
141 }
142}
143
144impl WebView {
145 pub(crate) fn new(mut builder: WebViewBuilder) -> Self {
146 let servo = builder.servo;
147 let painter_id = servo
148 .paint_mut()
149 .register_rendering_context(builder.rendering_context.clone());
150
151 let id = WebViewId::new(painter_id);
152 let webview = Self(Rc::new(RefCell::new(WebViewInner {
153 id,
154 servo: servo.clone(),
155 rendering_context: builder.rendering_context,
156 delegate: builder.delegate,
157 clipboard_delegate: builder
158 .clipboard_delegate
159 .unwrap_or_else(|| Rc::new(DefaultClipboardDelegate)),
160 #[cfg(feature = "gamepad")]
161 gamepad_delegate: builder
162 .gamepad_delegate
163 .unwrap_or_else(|| Rc::new(DefaultGamepadDelegate)),
164 accesskit_tree_id: None,
165 grafted_accesskit_tree_id: None,
166 grafted_accesskit_tree_epoch: None,
167 hidpi_scale_factor: builder.hidpi_scale_factor,
168 load_status: LoadStatus::Started,
169 status_text: None,
170 page_title: None,
171 favicon: None,
172 focused: false,
173 animating: false,
174 cursor: Cursor::Pointer,
175 back_forward_list: Default::default(),
176 back_forward_list_index: 0,
177 user_content_manager: builder.user_content_manager.clone(),
178 })));
179
180 let viewport_details = webview.viewport_details();
181 servo.paint().add_webview(
182 Box::new(ServoRendererWebView {
183 weak_handle: webview.weak_handle(),
184 id,
185 }),
186 viewport_details,
187 );
188
189 servo
190 .webviews_mut()
191 .insert(webview.id(), webview.weak_handle());
192
193 let user_content_manager_id = builder
194 .user_content_manager
195 .as_ref()
196 .map(|user_content_manager| user_content_manager.id());
197
198 let new_webview_details = NewWebViewDetails {
199 webview_id: webview.id(),
200 viewport_details,
201 user_content_manager_id,
202 };
203
204 match builder.create_new_webview_responder.as_mut() {
210 Some(responder) => {
211 let _ = responder.send(Some(new_webview_details));
212 },
213 None => {
214 let url = builder.url.unwrap_or(
215 Url::parse("about:blank")
216 .expect("Should always be able to parse 'about:blank'."),
217 );
218
219 servo
220 .constellation_proxy()
221 .send(EmbedderToConstellationMessage::NewWebView(
222 url.into(),
223 new_webview_details,
224 ));
225 },
226 }
227
228 webview
229 }
230
231 fn inner(&self) -> Ref<'_, WebViewInner> {
232 self.0.borrow()
233 }
234
235 fn inner_mut(&self) -> RefMut<'_, WebViewInner> {
236 self.0.borrow_mut()
237 }
238
239 pub(crate) fn request_create_new(
240 &self,
241 response_sender: GenericSender<Option<NewWebViewDetails>>,
242 ) {
243 let request = CreateNewWebViewRequest {
244 servo: self.inner().servo.clone(),
245 responder: IpcResponder::new(response_sender, None),
246 };
247 self.delegate().request_create_new(self.clone(), request);
248 }
249
250 pub(crate) fn viewport_details(&self) -> ViewportDetails {
251 let inner = self.inner();
254 let viewport_size = inner.rendering_context.size2d().to_f32();
255 let scaled_viewport_size = viewport_size / inner.hidpi_scale_factor;
256 let device_size = self
257 .delegate()
258 .screen_geometry(self.clone())
259 .map(|geometry| geometry.size.to_f32())
260 .unwrap_or_else(|| viewport_size);
261 ViewportDetails {
262 size: scaled_viewport_size / Scale::new(1.0),
263 hidpi_scale_factor: Scale::new(inner.hidpi_scale_factor.0),
264 device_size,
265 }
266 }
267
268 pub(crate) fn from_weak_handle(inner: &Weak<RefCell<WebViewInner>>) -> Option<Self> {
269 inner.upgrade().map(WebView)
270 }
271
272 pub(crate) fn weak_handle(&self) -> Weak<RefCell<WebViewInner>> {
273 Rc::downgrade(&self.0)
274 }
275
276 pub fn delegate(&self) -> Rc<dyn WebViewDelegate> {
278 self.inner().delegate.clone()
279 }
280
281 pub fn clipboard_delegate(&self) -> Rc<dyn ClipboardDelegate> {
283 self.inner().clipboard_delegate.clone()
284 }
285
286 #[cfg(feature = "gamepad")]
288 pub fn gamepad_delegate(&self) -> Rc<dyn GamepadDelegate> {
289 self.inner().gamepad_delegate.clone()
290 }
291
292 pub fn id(&self) -> WebViewId {
294 self.inner().id
295 }
296
297 pub fn rendering_context(&self) -> Rc<dyn RenderingContext> {
299 self.inner().rendering_context.clone()
300 }
301
302 pub fn load_status(&self) -> LoadStatus {
307 self.inner().load_status
308 }
309
310 pub(crate) fn set_load_status(self, new_value: LoadStatus) {
311 if self.inner().load_status == new_value {
312 return;
313 }
314 self.inner_mut().load_status = new_value;
315 self.delegate().notify_load_status_changed(self, new_value);
316 }
317
318 pub fn url(&self) -> Option<Url> {
321 let inner = self.inner();
322 inner
323 .back_forward_list
324 .get(inner.back_forward_list_index)
325 .cloned()
326 }
327
328 pub fn status_text(&self) -> Option<String> {
334 self.inner().status_text.clone()
335 }
336
337 pub(crate) fn set_status_text(self, new_value: Option<String>) {
338 if self.inner().status_text == new_value {
339 return;
340 }
341 self.inner_mut().status_text = new_value.clone();
342 self.delegate().notify_status_text_changed(self, new_value);
343 }
344
345 pub fn page_title(&self) -> Option<String> {
351 self.inner().page_title.clone()
352 }
353
354 pub(crate) fn set_page_title(self, new_value: Option<String>) {
355 if self.inner().page_title == new_value {
356 return;
357 }
358 self.inner_mut().page_title = new_value.clone();
359 self.delegate().notify_page_title_changed(self, new_value);
360 }
361
362 pub fn favicon(&self) -> Option<Ref<'_, Image>> {
369 Ref::filter_map(self.inner(), |inner| inner.favicon.as_ref()).ok()
370 }
371
372 pub(crate) fn set_favicon(self, new_value: Image) {
373 self.inner_mut().favicon = Some(new_value);
374 self.delegate().notify_favicon_changed(self);
375 }
376
377 pub fn focused(&self) -> bool {
382 self.inner().focused
383 }
384
385 pub(crate) fn set_focused(self, new_value: bool) {
386 if self.inner().focused == new_value {
387 return;
388 }
389 self.inner_mut().focused = new_value;
390 self.delegate().notify_focus_changed(self, new_value);
391 }
392
393 pub fn cursor(&self) -> Cursor {
399 self.inner().cursor
400 }
401
402 pub(crate) fn set_cursor(self, new_value: Cursor) {
403 if self.inner().cursor == new_value {
404 return;
405 }
406 self.inner_mut().cursor = new_value;
407 self.delegate().notify_cursor_changed(self, new_value);
408 }
409
410 pub fn focus(&self) {
412 self.inner()
413 .servo
414 .constellation_proxy()
415 .send(EmbedderToConstellationMessage::FocusWebView(self.id()));
416 }
417
418 pub fn blur(&self) {
420 self.inner()
421 .servo
422 .constellation_proxy()
423 .send(EmbedderToConstellationMessage::BlurWebView);
424 }
425
426 pub fn animating(&self) -> bool {
431 self.inner().animating
432 }
433
434 pub(crate) fn set_animating(self, new_value: bool) {
435 if self.inner().animating == new_value {
436 return;
437 }
438 self.inner_mut().animating = new_value;
439 self.delegate().notify_animating_changed(self, new_value);
440 }
441
442 pub fn size(&self) -> DeviceSize {
444 self.inner().rendering_context.size2d().to_f32()
445 }
446
447 pub fn resize(&self, new_size: PhysicalSize<u32>) {
454 let new_size = PhysicalSize {
455 width: new_size.width.max(MINIMUM_WEBVIEW_SIZE.width as u32),
456 height: new_size.height.max(MINIMUM_WEBVIEW_SIZE.height as u32),
457 };
458
459 self.inner()
460 .servo
461 .paint()
462 .resize_rendering_context(self.id(), new_size);
463 }
464
465 pub fn hidpi_scale_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> {
467 self.inner().hidpi_scale_factor
468 }
469
470 pub fn set_hidpi_scale_factor(
475 &self,
476 new_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
477 ) {
478 if self.inner().hidpi_scale_factor == new_scale_factor {
479 return;
480 }
481
482 self.inner_mut().hidpi_scale_factor = new_scale_factor;
483 self.inner()
484 .servo
485 .paint()
486 .set_hidpi_scale_factor(self.id(), new_scale_factor);
487 }
488
489 pub fn show(&self) {
491 self.inner()
492 .servo
493 .paint()
494 .show_webview(self.id())
495 .expect("BUG: invalid WebView instance");
496 }
497
498 pub fn hide(&self) {
500 self.inner()
501 .servo
502 .paint()
503 .hide_webview(self.id())
504 .expect("BUG: invalid WebView instance");
505 }
506
507 pub fn notify_theme_change(&self, theme: Theme) {
509 self.inner()
510 .servo
511 .constellation_proxy()
512 .send(EmbedderToConstellationMessage::ThemeChange(
513 self.id(),
514 theme,
515 ))
516 }
517
518 pub fn load(&self, url: Url) {
523 self.inner()
524 .servo
525 .constellation_proxy()
526 .send(EmbedderToConstellationMessage::LoadUrl(
527 self.id(),
528 UrlRequest::new(url),
529 ))
530 }
531
532 pub fn load_request(&self, url_request: UrlRequest) {
537 self.inner()
538 .servo
539 .constellation_proxy()
540 .send(EmbedderToConstellationMessage::LoadUrl(
541 self.id(),
542 url_request,
543 ))
544 }
545
546 pub fn reload(&self) {
548 self.inner_mut().load_status = LoadStatus::Started;
549 self.inner()
550 .servo
551 .constellation_proxy()
552 .send(EmbedderToConstellationMessage::Reload(self.id()))
553 }
554
555 pub fn can_go_back(&self) -> bool {
560 self.inner().back_forward_list_index != 0
561 }
562
563 pub fn go_back(&self, amount: usize) -> TraversalId {
569 let request = SessionHistoryTraversalRequest::new(
570 self.id(),
571 TraversalDirection::Back(amount),
572 HistoryTraversalSource::Embedder,
573 );
574 let traversal_id = request.id.clone();
575 self.inner()
576 .servo
577 .constellation_proxy()
578 .send(EmbedderToConstellationMessage::TraverseHistory(request));
579 traversal_id
580 }
581
582 pub fn can_go_forward(&self) -> bool {
587 let inner = self.inner();
588 inner.back_forward_list.len() > inner.back_forward_list_index + 1
589 }
590
591 pub fn go_forward(&self, amount: usize) -> TraversalId {
597 let request = SessionHistoryTraversalRequest::new(
598 self.id(),
599 TraversalDirection::Forward(amount),
600 HistoryTraversalSource::Embedder,
601 );
602 let traversal_id = request.id.clone();
603 self.inner()
604 .servo
605 .constellation_proxy()
606 .send(EmbedderToConstellationMessage::TraverseHistory(request));
607 traversal_id
608 }
609
610 pub fn notify_scroll_event(&self, scroll: Scroll, point: WebViewPoint) {
613 self.inner()
614 .servo
615 .paint()
616 .notify_scroll_event(self.id(), scroll, point);
617 }
618
619 pub fn notify_input_event(&self, event: InputEvent) -> InputEventId {
626 let event: InputEventAndId = event.into();
627 let event_id = event.id;
628 let webview_id = self.id();
629 let servo = &self.inner().servo;
630 if event.event.point().is_some() {
632 if !servo.paint().notify_input_event(self.id(), event) {
633 servo.add_pending_handled_input_event(PendingHandledInputEvent {
634 event_id,
635 webview_id,
636 });
637 servo.event_loop_waker().wake();
638 }
639 } else {
640 servo
641 .constellation_proxy()
642 .send(EmbedderToConstellationMessage::ForwardInputEvent(
643 webview_id, event, None, ));
645 }
646
647 event_id
648 }
649
650 pub fn notify_media_session_action_event(&self, event: MediaSessionActionType) {
652 self.inner()
653 .servo
654 .constellation_proxy()
655 .send(EmbedderToConstellationMessage::MediaSessionAction(event));
656 }
657
658 pub fn set_page_zoom(&self, new_zoom: f32) {
668 self.inner()
669 .servo
670 .paint()
671 .set_page_zoom(self.id(), new_zoom);
672 }
673
674 pub fn page_zoom(&self) -> f32 {
676 self.inner().servo.paint().page_zoom(self.id())
677 }
678
679 pub fn adjust_pinch_zoom(&self, pinch_zoom_delta: f32, center: DevicePoint) {
690 self.inner()
691 .servo
692 .paint()
693 .adjust_pinch_zoom(self.id(), pinch_zoom_delta, center);
694 }
695
696 pub fn pinch_zoom(&self) -> f32 {
698 self.inner().servo.paint().pinch_zoom(self.id())
699 }
700
701 pub fn device_pixels_per_css_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> {
706 self.inner()
707 .servo
708 .paint()
709 .device_pixels_per_page_pixel(self.id())
710 }
711
712 pub fn exit_fullscreen(&self) {
714 self.inner()
715 .servo
716 .constellation_proxy()
717 .send(EmbedderToConstellationMessage::ExitFullScreen(self.id()));
718 }
719
720 pub fn set_throttled(&self, throttled: bool) {
725 self.inner().servo.constellation_proxy().send(
726 EmbedderToConstellationMessage::SetWebViewThrottled(self.id(), throttled),
727 );
728 }
729
730 pub fn toggle_webrender_debugging(&self, debugging: WebRenderDebugOption) {
736 self.inner().servo.paint().toggle_webrender_debug(debugging);
737 }
738
739 pub fn capture_webrender(&self) {
744 self.inner().servo.paint().capture_webrender(self.id());
745 }
746
747 pub fn paint(&self) {
749 self.inner().servo.paint().render(self.id());
750 }
751
752 pub fn user_content_manager(&self) -> Option<Rc<UserContentManager>> {
754 self.inner().user_content_manager.clone()
755 }
756
757 pub fn evaluate_javascript<T: ToString>(
760 &self,
761 script: T,
762 callback: impl FnOnce(Result<JSValue, JavaScriptEvaluationError>) + 'static,
763 ) {
764 self.inner().servo.javascript_evaluator_mut().evaluate(
765 self.id(),
766 script.to_string(),
767 Box::new(callback),
768 );
769 }
770
771 pub fn take_screenshot(
789 &self,
790 rect: Option<WebViewRect>,
791 callback: impl FnOnce(Result<RgbaImage, ScreenshotCaptureError>) + 'static,
792 ) {
793 self.inner()
794 .servo
795 .paint()
796 .request_screenshot(self.id(), rect, Box::new(callback));
797 }
798
799 pub(crate) fn set_history(self, new_back_forward_list: Vec<ServoUrl>, new_index: usize) {
800 {
801 let mut inner_mut = self.inner_mut();
802 inner_mut.back_forward_list_index = new_index;
803 inner_mut.back_forward_list = new_back_forward_list
804 .into_iter()
805 .map(ServoUrl::into_url)
806 .collect();
807 }
808
809 let back_forward_list = self.inner().back_forward_list.clone();
810 let back_forward_list_index = self.inner().back_forward_list_index;
811 self.delegate().notify_url_changed(
812 self.clone(),
813 back_forward_list[back_forward_list_index].clone(),
814 );
815 self.delegate().notify_history_changed(
816 self.clone(),
817 back_forward_list,
818 back_forward_list_index,
819 );
820 }
821
822 pub(crate) fn show_embedder_control(
823 self,
824 control_id: EmbedderControlId,
825 position: DeviceIntRect,
826 embedder_control_request: EmbedderControlRequest,
827 ) {
828 let constellation_proxy = self.inner().servo.constellation_proxy().clone();
829 let embedder_control = match embedder_control_request {
830 EmbedderControlRequest::SelectElement(request) => {
831 EmbedderControl::SelectElement(SelectElement {
832 id: control_id,
833 select_element_request: request,
834 position,
835 constellation_proxy,
836 response_sent: false,
837 })
838 },
839 EmbedderControlRequest::ColorPicker(current_color) => {
840 EmbedderControl::ColorPicker(ColorPicker {
841 id: control_id,
842 current_color: Some(current_color),
843 position,
844 constellation_proxy,
845 response_sent: false,
846 })
847 },
848 EmbedderControlRequest::InputMethod(input_method_request) => {
849 EmbedderControl::InputMethod(InputMethodControl {
850 id: control_id,
851 input_method_type: input_method_request.input_method_type,
852 text: input_method_request.text,
853 insertion_point: input_method_request.insertion_point,
854 position,
855 multiline: input_method_request.multiline,
856 allow_virtual_keyboard: input_method_request.allow_virtual_keyboard,
857 })
858 },
859 EmbedderControlRequest::ContextMenu(mut context_menu_request) => {
860 for item in context_menu_request.items.iter_mut() {
861 match item {
862 ContextMenuItem::Item {
863 action: ContextMenuAction::GoBack,
864 enabled,
865 ..
866 } => *enabled = self.can_go_back(),
867 ContextMenuItem::Item {
868 action: ContextMenuAction::GoForward,
869 enabled,
870 ..
871 } => *enabled = self.can_go_forward(),
872 _ => {},
873 }
874 }
875 EmbedderControl::ContextMenu(ContextMenu {
876 id: control_id,
877 position,
878 items: context_menu_request.items,
879 element_info: context_menu_request.element_info,
880 constellation_proxy,
881 response_sent: false,
882 })
883 },
884 EmbedderControlRequest::FilePicker { .. } => {
885 unreachable!("This message should be routed through the FileManagerThread")
886 },
887 };
888
889 self.delegate()
890 .show_embedder_control(self.clone(), embedder_control);
891 }
892
893 pub fn accesskit_tree_id(&self) -> Option<TreeId> {
895 self.inner().accesskit_tree_id
896 }
897
898 pub fn set_accessibility_active(&self, active: bool) -> Option<TreeId> {
913 if !pref!(accessibility_enabled) {
914 return None;
915 }
916
917 if active == self.inner().accesskit_tree_id.is_some() {
918 return self.accesskit_tree_id();
919 }
920
921 if active {
922 let accesskit_tree_id = TreeId(AccesskitUuid::new_v4());
923 self.inner_mut().accesskit_tree_id = Some(accesskit_tree_id);
924 } else {
925 self.inner_mut().accesskit_tree_id = None;
926 self.inner_mut().grafted_accesskit_tree_id = None;
927 self.inner_mut().grafted_accesskit_tree_epoch = None;
928 }
929
930 self.inner().servo.constellation_proxy().send(
931 EmbedderToConstellationMessage::SetAccessibilityActive(self.id(), active),
932 );
933
934 self.accesskit_tree_id()
935 }
936
937 pub(crate) fn notify_document_accessibility_tree_id(&self, grafted_tree_id: TreeId) {
938 let Some(webview_accesskit_tree_id) = self.inner().accesskit_tree_id else {
939 return;
940 };
941 let old_grafted_tree_id = self
942 .inner_mut()
943 .grafted_accesskit_tree_id
944 .replace(grafted_tree_id);
945 if old_grafted_tree_id == Some(grafted_tree_id) {
948 return;
949 }
950 let root_node_id = NodeId(0);
951 let mut root_node = AccesskitNode::new(Role::ScrollView);
952 let graft_node_id = NodeId(1);
953 let mut graft_node = AccesskitNode::new(Role::GenericContainer);
954 graft_node.set_tree_id(grafted_tree_id);
955 root_node.set_children(vec![graft_node_id]);
956 self.delegate().notify_accessibility_tree_update(
957 self.clone(),
958 TreeUpdate {
959 nodes: vec![(root_node_id, root_node), (graft_node_id, graft_node)],
960 tree: Some(Tree {
961 root: root_node_id,
962 toolkit_name: None,
963 toolkit_version: None,
964 }),
965 tree_id: webview_accesskit_tree_id,
966 focus: root_node_id,
967 },
968 );
969 }
970
971 pub(crate) fn process_accessibility_tree_update(&self, tree_update: TreeUpdate, epoch: Epoch) {
972 if self
973 .inner()
974 .grafted_accesskit_tree_epoch
975 .is_some_and(|current| epoch < current)
976 {
977 debug!("Ignoring stale tree update for {:?}", tree_update.tree_id);
981 return;
982 }
983 if self
984 .inner()
985 .grafted_accesskit_tree_epoch
986 .is_none_or(|current| epoch > current)
987 {
988 self.notify_document_accessibility_tree_id(tree_update.tree_id);
989 self.inner_mut().grafted_accesskit_tree_epoch = Some(epoch);
990 }
991 self.delegate()
992 .notify_accessibility_tree_update(self.clone(), tree_update);
993 }
994}
995
996struct ServoRendererWebView {
999 id: WebViewId,
1000 weak_handle: Weak<RefCell<WebViewInner>>,
1001}
1002
1003impl WebViewTrait for ServoRendererWebView {
1004 fn id(&self) -> WebViewId {
1005 self.id
1006 }
1007
1008 fn screen_geometry(&self) -> Option<ScreenGeometry> {
1009 let webview = WebView::from_weak_handle(&self.weak_handle)?;
1010 webview.delegate().screen_geometry(webview)
1011 }
1012
1013 fn set_animating(&self, new_value: bool) {
1014 if let Some(webview) = WebView::from_weak_handle(&self.weak_handle) {
1015 webview.set_animating(new_value);
1016 }
1017 }
1018}
1019
1020pub struct WebViewBuilder {
1022 servo: Servo,
1023 rendering_context: Rc<dyn RenderingContext>,
1024 delegate: Rc<dyn WebViewDelegate>,
1025 url: Option<Url>,
1026 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1027 create_new_webview_responder: Option<IpcResponder<Option<NewWebViewDetails>>>,
1028 user_content_manager: Option<Rc<UserContentManager>>,
1029 clipboard_delegate: Option<Rc<dyn ClipboardDelegate>>,
1030 #[cfg(feature = "gamepad")]
1031 gamepad_delegate: Option<Rc<dyn GamepadDelegate>>,
1032}
1033
1034impl WebViewBuilder {
1035 pub fn new(servo: &Servo, rendering_context: Rc<dyn RenderingContext>) -> Self {
1040 Self {
1041 servo: servo.clone(),
1042 rendering_context,
1043 url: None,
1044 hidpi_scale_factor: Scale::new(1.0),
1045 delegate: Rc::new(DefaultWebViewDelegate),
1046 create_new_webview_responder: None,
1047 user_content_manager: None,
1048 clipboard_delegate: None,
1049 #[cfg(feature = "gamepad")]
1050 gamepad_delegate: None,
1051 }
1052 }
1053
1054 pub(crate) fn new_for_create_request(
1055 servo: &Servo,
1056 rendering_context: Rc<dyn RenderingContext>,
1057 responder: IpcResponder<Option<NewWebViewDetails>>,
1058 ) -> Self {
1059 let mut builder = Self::new(servo, rendering_context);
1060 builder.create_new_webview_responder = Some(responder);
1061 builder
1062 }
1063
1064 pub fn delegate(mut self, delegate: Rc<dyn WebViewDelegate>) -> Self {
1067 self.delegate = delegate;
1068 self
1069 }
1070
1071 pub fn url(mut self, url: Url) -> Self {
1073 self.url = Some(url);
1074 self
1075 }
1076
1077 pub fn hidpi_scale_factor(
1079 mut self,
1080 hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1081 ) -> Self {
1082 self.hidpi_scale_factor = hidpi_scale_factor;
1083 self
1084 }
1085
1086 pub fn user_content_manager(mut self, user_content_manager: Rc<UserContentManager>) -> Self {
1090 self.user_content_manager = Some(user_content_manager);
1091 self
1092 }
1093
1094 pub fn clipboard_delegate(mut self, clipboard_delegate: Rc<dyn ClipboardDelegate>) -> Self {
1097 self.clipboard_delegate = Some(clipboard_delegate);
1098 self
1099 }
1100
1101 #[cfg(feature = "gamepad")]
1104 pub fn gamepad_delegate(mut self, gamepad_delegate: Rc<dyn GamepadDelegate>) -> Self {
1105 self.gamepad_delegate = Some(gamepad_delegate);
1106 self
1107 }
1108
1109 pub fn build(self) -> WebView {
1111 WebView::new(self)
1112 }
1113}